#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 <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/IOMessage.h>
#include <mach/mach_time.h>
#include <mach/mach.h>
#include <mach/message.h>
#include <mach/boolean.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.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 "PPPControllerPriv.h"
#include "ppp_msg.h"
#include "ppp_privmsg.h"
#include "../Family/ppp_domain.h"
#include "../Helpers/pppd/pppd.h"
#include "ppp_client.h"
#include "ppp_manager.h"
#include "ppp_option.h"
#include "ppp_socket_server.h"
#include "ppp_utils.h"
#define CREATESERVICESETUP(a) SCDynamicStoreKeyCreateNetworkServiceEntity(0, \
kSCDynamicStoreDomainSetup, kSCCompAnyRegex, a)
#define CREATESERVICESTATE(a) SCDynamicStoreKeyCreateNetworkServiceEntity(0, \
kSCDynamicStoreDomainState, kSCCompAnyRegex, a)
#define CREATEPREFIXSETUP() SCDynamicStoreKeyCreate(0, CFSTR("%@/%@/%@/"), \
kSCDynamicStoreDomainSetup, kSCCompNetwork, kSCCompService)
#define CREATEPREFIXSTATE() SCDynamicStoreKeyCreate(0, CFSTR("%@/%@/%@/"), \
kSCDynamicStoreDomainState, kSCCompNetwork, kSCCompService)
#define CREATEGLOBALSETUP(a) SCDynamicStoreKeyCreateNetworkGlobalEntity(0, \
kSCDynamicStoreDomainSetup, a)
#define CREATEGLOBALSTATE(a) SCDynamicStoreKeyCreateNetworkGlobalEntity(0, \
kSCDynamicStoreDomainState, a)
enum {
READ = 0, WRITE = 1 };
#define MAX_EXTRACONNECTTIME 20
#define MIN_EXTRACONNECTTIME 3
TAILQ_HEAD(, ppp) ppp_head;
io_connect_t gIOPort;
int gSleeping;
long gSleepArgument;
CFUserNotificationRef gSleepNotification;
uint64_t gWakeUpTime = 0;
double gTimeScaleSeconds;
CFRunLoopSourceRef gStopRls;
CFStringRef gLoggedInUser = NULL;
uid_t gLoggedInUserUID = 0;
SCDynamicStoreRef gDynamicStore = NULL;
static void display_error(struct ppp *ppp);
static void iosleep_notifier(void * x, io_service_t y, natural_t messageType, void *messageArgument);
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 can_sleep();
static int will_sleep(int checking);
static void wake_up();
static void ipv4_state_changed();
static int is_idle();
static int log_out();
static int log_in();
static int log_switch();
static int send_pppd_params(struct ppp *ppp, CFDictionaryRef service, CFDictionaryRef options, u_int8_t dialondemand);
static int change_pppd_params(struct ppp *ppp, CFDictionaryRef service, CFDictionaryRef options);
static u_short findfreeunit(u_short subtype);
static int add_client(struct ppp *ppp, void *client, int autoclose);
static int remove_client(struct ppp *ppp, void *client);
static struct ppp_client *get_client(struct ppp *ppp, void *client);
static int remove_all_clients(struct ppp *ppp);
static struct ppp * new_service(CFStringRef serviceID , CFStringRef subtypeRef);
static int dispose_service(struct ppp *ppp);
static int setup_service(CFStringRef serviceID);
static void store_notifier(SCDynamicStoreRef session, CFArrayRef changedKeys, void *info);
static void reorder_services();
static void finish_setup_services();
static void setup_PPPoE(struct ppp *ppp);
static void dispose_PPPoE(struct ppp *ppp);
#ifdef DEBUG
static void print_services();
#endif
u_long ppp_init_all() {
IONotificationPortRef notify;
io_object_t iterator;
mach_timebase_info_data_t timebaseInfo;
CFStringRef key = NULL, setup = NULL;
CFMutableArrayRef keys = NULL, patterns = NULL;
CFArrayRef services = NULL;
int i, nb, ret = 0;
CFRunLoopSourceRef rls = NULL;
CFStringRef notifs[] = {
kSCEntNetPPP,
kSCEntNetModem,
kSCEntNetInterface,
kSCEntNetIPv4,
kSCEntNetIPv6,
kSCEntNetDNS,
NULL,
};
TAILQ_INIT(&ppp_head);
gSleeping = 0;
gSleepNotification = 0;
gStopRls = 0;
gIOPort = IORegisterForSystemPower(0, ¬ify, iosleep_notifier, &iterator);
if (gIOPort == 0) {
printf("IORegisterForSystemPower failed\n");
goto fail;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(notify),
kCFRunLoopDefaultMode);
if (mach_timebase_info(&timebaseInfo) != KERN_SUCCESS) { printf("mach_timebase_info failed\n");
goto fail;
}
gTimeScaleSeconds = ((double) timebaseInfo.numer / (double) timebaseInfo.denom) / 1000000000;
if ((gDynamicStore = SCDynamicStoreCreate(0, CFSTR("PPPController"), store_notifier, 0)) == NULL)
goto fail;
if ((rls = SCDynamicStoreCreateRunLoopSource(0, gDynamicStore, 0)) == NULL)
goto fail;
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
gLoggedInUser = SCDynamicStoreCopyConsoleUser(gDynamicStore, &gLoggedInUserUID, 0);
keys = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
patterns = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
if (keys == NULL || patterns == NULL)
goto fail;
for (i = 0; notifs[i]; i++) {
if ((key = CREATESERVICESETUP(notifs[i])) == NULL)
goto fail;
CFArrayAppendValue(patterns, key);
CFRelease(key);
}
if ((key = CREATEGLOBALSETUP(kSCEntNetIPv4)) == NULL)
goto fail;
CFArrayAppendValue(keys, key);
CFRelease(key);
if ((key = CREATEGLOBALSTATE(kSCEntNetIPv4)) == NULL)
goto fail;
CFArrayAppendValue(keys, key);
CFRelease(key);
if ((key = SCDynamicStoreKeyCreateConsoleUser(0)) == NULL)
goto fail;
CFArrayAppendValue(keys, key);
CFRelease(key);
SCDynamicStoreSetNotificationKeys(gDynamicStore, keys, patterns);
key = CREATESERVICESETUP(kSCEntNetPPP);
setup = CREATEPREFIXSETUP();
if (key == NULL || setup == NULL)
goto fail;
services = SCDynamicStoreCopyKeyList(gDynamicStore, key);
if (services == NULL)
goto done;
nb = CFArrayGetCount(services);
for (i = 0; i < nb; i++) {
CFStringRef serviceID;
if (serviceID = parse_component(CFArrayGetValueAtIndex(services, i), setup)) {
setup_service(serviceID);
CFRelease(serviceID);
}
}
reorder_services();
finish_setup_services();
done:
my_CFRelease(services);
my_CFRelease(key);
my_CFRelease(setup);
my_CFRelease(keys);
my_CFRelease(patterns);
return ret;
fail:
my_CFRelease(gDynamicStore);
ret = -1;
goto done;
}
void ppp_dispose_all()
{
}
static
int setup_service(CFStringRef serviceID)
{
CFDictionaryRef service = NULL, interface;
CFStringRef subtype = NULL, iftype = NULL;
struct ppp *ppp;
ppp = ppp_findbyserviceID(serviceID);
interface = copyEntity(kSCDynamicStoreDomainSetup, serviceID, kSCEntNetInterface);
if (interface)
iftype = CFDictionaryGetValue(interface, kSCPropNetInterfaceType);
if (!interface ||
(iftype && !CFEqual(iftype, kSCValNetInterfaceTypePPP))) {
if (ppp) {
dispose_service(ppp);
}
goto done;
}
service = copyEntity(kSCDynamicStoreDomainSetup, serviceID, kSCEntNetPPP);
if (!service) goto done;
subtype = CFDictionaryGetValue(interface, kSCPropNetInterfaceSubType);
if (!subtype)
goto done;
if (ppp && !CFEqual(subtype, ppp->subtypeRef)) {
dispose_service(ppp);
ppp = 0;
}
if (!ppp) {
ppp = new_service(serviceID, subtype);
if (!ppp)
goto done;
}
ppp->flags |= FLAG_SETUP;
done:
my_CFRelease(interface);
my_CFRelease(service);
return 0;
}
static
void reorder_services()
{
CFDictionaryRef ip_dict = NULL;
CFStringRef key, serviceID;
CFArrayRef serviceorder;
int i, nb;
struct ppp *ppp;
key = CREATEGLOBALSETUP(kSCEntNetIPv4);
if (key) {
ip_dict = (CFDictionaryRef)SCDynamicStoreCopyValue(gDynamicStore, key);
if (ip_dict) {
serviceorder = CFDictionaryGetValue(ip_dict, kSCPropNetServiceOrder);
if (serviceorder) {
nb = CFArrayGetCount(serviceorder);
for (i = 0; i < nb; i++) {
serviceID = CFArrayGetValueAtIndex(serviceorder, i);
if (ppp = ppp_findbyserviceID(serviceID)) {
TAILQ_REMOVE(&ppp_head, ppp, next);
TAILQ_INSERT_TAIL(&ppp_head, ppp, next);
}
}
}
CFRelease(ip_dict);
}
CFRelease(key);
}
}
static
void store_notifier(SCDynamicStoreRef session, CFArrayRef changedKeys, void *info)
{
CFStringRef setup, userkey, ipsetupkey, ipstatekey;
u_long i, nb, doreorder = 0, dopostsetup = 0;
if (changedKeys == NULL)
return;
setup = CREATEPREFIXSETUP();
userkey = SCDynamicStoreKeyCreateConsoleUser(0);
ipsetupkey = CREATEGLOBALSETUP(kSCEntNetIPv4);
ipstatekey = CREATEGLOBALSTATE(kSCEntNetIPv4);
if (setup == NULL || userkey == NULL || ipsetupkey == NULL || ipstatekey == NULL) {
#ifdef DEBUG
SCLog(TRUE, LOG_ERR, CFSTR("PPPController cache_notifier : can't allocate keys\n"));
#endif
goto done;
}
nb = CFArrayGetCount(changedKeys);
for (i = 0; i < nb; i++) {
CFStringRef change, serviceID;
change = CFArrayGetValueAtIndex(changedKeys, i);
if (CFEqual(change, userkey)) {
CFStringRef olduser = gLoggedInUser;
gLoggedInUser = SCDynamicStoreCopyConsoleUser(session, &gLoggedInUserUID, 0);
if (gLoggedInUser == 0)
log_out(); else if (olduser == 0)
log_in(); else
log_switch(); if (olduser) CFRelease(olduser);
continue;
}
if (CFEqual(change, ipsetupkey)) {
doreorder = 1;
continue;
}
if (CFEqual(change, ipstatekey)) {
ipv4_state_changed();
continue;
}
serviceID = parse_component(change, setup);
if (serviceID) {
setup_service(serviceID);
CFRelease(serviceID);
dopostsetup = 1;
continue;
}
}
if (doreorder) {
reorder_services();
}
if (dopostsetup)
finish_setup_services();
done:
my_CFRelease(setup);
my_CFRelease(userkey);
my_CFRelease(ipsetupkey);
my_CFRelease(ipstatekey);
return;
}
void ppp_stop_all(CFRunLoopSourceRef stopRls)
{
u_int32_t delay = 0;
struct ppp *ppp;
if (gStopRls)
return;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (ppp->phase != PPP_IDLE) {
delay = 1;
ppp_disconnect(ppp, 0, SIGTERM);
}
}
if (delay)
gStopRls = stopRls;
else
CFRunLoopSourceSignal(stopRls);
}
u_int32_t ppp_makeref(struct ppp *ppp)
{
return (((u_long)ppp->subtype) << 16) + ppp->unit;
}
struct ppp *ppp_findbyserviceID(CFStringRef serviceID)
{
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next)
if (CFStringCompare(ppp->serviceID, serviceID, 0) == kCFCompareEqualTo)
return ppp;
return 0;
}
struct ppp *ppp_findbysid(u_char *data, int len)
{
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next)
if (ppp->sid && (strlen(ppp->sid) == len) && !strncmp(ppp->sid, data, len))
return ppp;
return 0;
}
struct ppp *ppp_findbypid(pid_t pid)
{
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next)
if (ppp->pid == pid)
return ppp;
return 0;
}
struct ppp *ppp_findbyref(u_long ref)
{
u_short subtype = ref >> 16;
u_short unit = ref & 0xFFFF;
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (((ppp->subtype == subtype) || (subtype == 0xFFFF))
&& ((ppp->unit == unit) || (unit == 0xFFFF))) {
return ppp;
}
}
return 0;
}
static
u_short findfreeunit(u_short subtype)
{
struct ppp *ppp = TAILQ_FIRST(&ppp_head);
u_short unit = 0;
while (ppp) {
if ((subtype == ppp->subtype)
&& (ppp->unit == unit)) {
unit++;
if (unit == 0xFFFF)
return unit;
ppp = TAILQ_FIRST(&ppp_head); }
else
ppp = TAILQ_NEXT(ppp, next); }
return unit;
}
#ifdef DEBUG
static
void print_services()
{
struct ppp *ppp;
SCLog(TRUE, LOG_INFO, CFSTR("Printing list of ppp services : \n"));
TAILQ_FOREACH(ppp, &ppp_head, next) {
SCLog(TRUE, LOG_INFO, CFSTR("Service : %@, subtype = %d\n"), ppp->serviceID, ppp->subtype);
}
}
#endif
static
struct ppp * new_service(CFStringRef serviceID , CFStringRef subtypeRef)
{
struct ppp *ppp;
u_short unit, subtype, len;
CFURLRef url;
u_char str[MAXPATHLEN], str2[32];
if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPSerial))
subtype = PPP_TYPE_SERIAL;
else if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPoE))
subtype = PPP_TYPE_PPPoE;
else if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPTP))
subtype = PPP_TYPE_PPTP;
else if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypeL2TP))
subtype = PPP_TYPE_L2TP;
else
subtype = PPP_TYPE_OTHER;
unit = findfreeunit(subtype);
if (unit == 0xFFFF)
return 0;
ppp = malloc(sizeof(struct ppp));
if (!ppp)
return 0;
bzero(ppp, sizeof(struct ppp));
ppp->serviceID = CFRetain(serviceID);
ppp->subtypeRef = CFRetain(subtypeRef);
len = CFStringGetLength(serviceID) + 1;
if (ppp->sid = malloc(len)) {
CFStringGetCString(serviceID, ppp->sid, len, kCFStringEncodingUTF8);
}
ppp->unit = unit;
ppp->pid = -1;
ppp->subtype = subtype;
ppp->phase = PPP_IDLE;
ppp->statusfd[READ] = -1;
ppp->statusfd[WRITE] = -1;
ppp->controlfd[READ] = -1;
ppp->controlfd[WRITE] = -1;
ppp->uid = 0;
ppp->ndrv_socket = -1;
ppp->flags |= FLAG_FIRSTDIAL;
strcpy(str, DIR_KEXT);
str2[0] = 0;
CFStringGetCString(ppp->subtypeRef, str2, sizeof(str2), kCFStringEncodingUTF8);
strcat(str, str2);
strcat(str, ".ppp"); url = CFURLCreateFromFileSystemRepresentation(NULL, str, strlen(str), TRUE);
if (url) {
ppp->bundle = CFBundleCreate(0, url);
CFRelease(url);
}
TAILQ_INIT(&ppp->client_head);
TAILQ_INSERT_TAIL(&ppp_head, ppp, next);
return ppp;
}
static
void display_error(struct ppp *ppp)
{
CFStringRef ppp_msg = NULL, dev_msg = NULL, msg = NULL;
CFPropertyListRef langRef = NULL;
if (ppp->laststatus == EXIT_USER_REQUEST)
return;
if ((ppp->flags & FLAG_ALERTERRORS) == 0)
return;
if (gLoggedInUser) {
CFPreferencesSynchronize(kCFPreferencesAnyApplication,
gLoggedInUser, kCFPreferencesAnyHost);
langRef = CFPreferencesCopyValue(CFSTR("AppleLanguages"),
kCFPreferencesAnyApplication, gLoggedInUser, kCFPreferencesAnyHost);
}
if (langRef == NULL) {
CFPreferencesSynchronize(kCFPreferencesAnyApplication,
kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
langRef = CFPreferencesCopyValue(CFSTR("AppleLanguages"),
kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
}
if (ppp->lastdevstatus && ppp->bundle) {
dev_msg = CFStringCreateWithFormat(0, 0, CFSTR("Device Error %d"), ppp->lastdevstatus);
if (dev_msg)
msg = CopyUserLocalizedString(ppp->bundle, dev_msg, dev_msg, langRef);
}
if (msg == NULL) {
ppp_msg = CFStringCreateWithFormat(0, 0, CFSTR("PPP Error %d"), ppp->laststatus);
if (ppp_msg)
msg = CopyUserLocalizedString(gBundleRef, ppp_msg, ppp_msg, langRef) ;
}
if (msg && CFStringGetLength(msg))
CFUserNotificationDisplayNotice(120, kCFUserNotificationNoteAlertLevel,
gIconURLRef, NULL, gBundleURLRef, CFSTR("Internet Connect"), msg, NULL);
my_CFRelease(msg);
my_CFRelease(ppp_msg);
my_CFRelease(dev_msg);
my_CFRelease(langRef);
}
static
void setup_PPPoE(struct ppp *ppp)
{
CFDictionaryRef interface;
CFStringRef hardware, device;
struct sockaddr_ndrv ndrv;
if (ppp->subtype == PPP_TYPE_PPPoE) {
interface = copyEntity(kSCDynamicStoreDomainSetup, ppp->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 (ppp->device
&& (CFStringCompare(device, ppp->device, 0) != kCFCompareEqualTo)) {
dispose_PPPoE(ppp);
}
if (!ppp->device) {
ppp->ndrv_socket = socket(PF_NDRV, SOCK_RAW, 0);
if (ppp->ndrv_socket >= 0) {
ppp->device = device;
CFRetain(ppp->device);
CFStringGetCString(device, ndrv.snd_name, sizeof(ndrv.snd_name), kCFStringEncodingMacRoman);
ndrv.snd_len = sizeof(ndrv);
ndrv.snd_family = AF_NDRV;
if (bind(ppp->ndrv_socket, (struct sockaddr *)&ndrv, sizeof(ndrv)) < 0) {
dispose_PPPoE(ppp);
}
}
}
}
else {
dispose_PPPoE(ppp);
}
CFRelease(interface);
}
}
}
static
void dispose_PPPoE(struct ppp *ppp)
{
if (ppp->ndrv_socket != -1) {
close(ppp->ndrv_socket);
ppp->ndrv_socket = -1;
}
if (ppp->device) {
CFRelease(ppp->device);
ppp->device = 0;
}
}
static
void finish_setup_services()
{
u_int32_t lval;
struct ppp *ppp;
CFDictionaryRef service;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (ppp->flags & FLAG_SETUP) {
ppp->flags &= ~(FLAG_FREE + FLAG_SETUP);
setup_PPPoE(ppp);
switch (ppp->phase) {
case PPP_IDLE:
if ((getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &lval) && lval)
&& (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &lval) || !lval
|| gLoggedInUser)) {
ppp_connect(ppp, 0, 1, 0, 0, 0, 0, 0);
}
break;
case PPP_DORMANT:
case PPP_HOLDOFF:
ppp->flags |= FLAG_CONFIGCHANGEDNOW;
ppp->flags &= ~FLAG_CONFIGCHANGEDLATER;
ppp_disconnect(ppp, 0, SIGTERM);
break;
default :
ppp->flags |= FLAG_CONFIGCHANGEDLATER;
ppp->flags &= ~FLAG_CONFIGCHANGEDNOW;
service = copyService(kSCDynamicStoreDomainSetup, ppp->serviceID);
if (service) {
change_pppd_params(ppp, service, ppp->connectopts);
CFRelease(service);
}
break;
}
}
}
}
static
void iosleep_notifier(void * x, io_service_t y, natural_t messageType, void *messageArgument)
{
CFMutableDictionaryRef dict;
SInt32 error;
int delay;
switch ( messageType ) {
case kIOMessageSystemWillSleep:
gSleeping = 1; gSleepArgument = (long)messageArgument;
delay = will_sleep(0);
if (delay == 0)
IOAllowPowerChange(gIOPort, (long)messageArgument);
else {
if (delay & 2) {
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (dict) {
CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, gIconURLRef);
CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, gBundleURLRef);
CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, CFSTR("Internet Connect"));
CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, CFSTR("Waiting for disconnection"));
gSleepNotification = CFUserNotificationCreate(NULL, 0,
kCFUserNotificationNoteAlertLevel
+ kCFUserNotificationNoDefaultButtonFlag, &error, dict);
CFRelease(dict);
}
}
}
break;
case kIOMessageCanSystemSleep:
if (can_sleep())
IOAllowPowerChange(gIOPort, (long)messageArgument);
else
IOCancelPowerChange(gIOPort, (long)messageArgument);
break;
case kIOMessageSystemWillNotSleep:
break;
case kIOMessageSystemWillPowerOn:
gSleeping = 0; gWakeUpTime = mach_absolute_time();
if (gSleepNotification) {
CFUserNotificationCancel(gSleepNotification);
CFRelease(gSleepNotification);
gSleepNotification = 0;
}
break;
case kIOMessageSystemHasPoweredOn:
wake_up();
break;
}
}
static
int can_sleep()
{
struct ppp *ppp;
u_int32_t prevent;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (ppp->phase == PPP_RUNNING) {
switch (ppp->subtype) {
case PPP_TYPE_PPTP:
case PPP_TYPE_L2TP:
prevent = 0;
break;
default :
prevent = 1;
}
getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, CFSTR("PreventIdleSleep"), &prevent);
if (prevent)
return 0;
}
}
return 1;
}
static
int will_sleep(int checking)
{
u_int32_t disc, delay = 0, alert = 0;
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (ppp->phase != PPP_IDLE
&& (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnSleep, &disc)
|| disc)) {
delay = 1;
if (ppp->phase != PPP_DORMANT || ppp->phase != PPP_HOLDOFF)
alert = 2;
if (!checking)
ppp_disconnect(ppp, 0, SIGTERM);
}
}
return delay + alert;
}
static
int is_idle()
{
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next)
if (ppp->phase != PPP_IDLE)
return 0;
return 1;
}
static
void ipv4_state_changed()
{
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next) {
ppp->flags |= FLAG_FIRSTDIAL;
}
}
static
void wake_up()
{
struct ppp *ppp;
u_int32_t lval;
TAILQ_FOREACH(ppp, &ppp_head, next) {
ppp->flags |= FLAG_FIRSTDIAL;
if (ppp->phase == PPP_IDLE) {
if ((getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &lval) && lval)
&& (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &lval) || !lval
|| gLoggedInUser)) {
ppp_connect(ppp, 0, 1, 0, 0, 0, 0, 0);
}
}
}
}
static
int log_out()
{
struct ppp *ppp;
u_int32_t disc;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (ppp->phase != PPP_IDLE
&& getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &disc)
&& disc)
ppp_disconnect(ppp, 0, SIGTERM);
}
return 0;
}
static
int log_in()
{
struct ppp *ppp;
u_int32_t val;
TAILQ_FOREACH(ppp, &ppp_head, next) {
ppp->flags |= FLAG_FIRSTDIAL;
if (ppp->phase == PPP_IDLE
&& getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &val)
&& val) {
ppp_connect(ppp, 0, 1, 0, 0, 0, 0, 0);
}
}
return 0;
}
static
int log_switch()
{
struct ppp *ppp;
u_int32_t disc, val, demand;
TAILQ_FOREACH(ppp, &ppp_head, next) {
ppp->flags |= FLAG_FIRSTDIAL;
demand = getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &val) && val;
switch (ppp->phase) {
case PPP_IDLE:
if (demand)
ppp_connect(ppp, 0, 1, 0, 0, 0, 0, 0);
break;
default:
disc = 0;
if (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, CFSTR("DisconnectOnFastUserSwitch"), &disc))
getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &disc);
if (disc) {
ppp->flags &= ~FLAG_CONFIGCHANGEDLATER;
if (demand)
ppp->flags |= FLAG_CONFIGCHANGEDNOW;
else
ppp->flags &= ~FLAG_CONFIGCHANGEDNOW;
ppp_disconnect(ppp, 0, SIGTERM);
}
}
}
return 0;
}
static
int dispose_service(struct ppp *ppp)
{
ppp_disconnect(ppp, 0, SIGTERM);
if (ppp->phase != PPP_IDLE) {
ppp->flags |= FLAG_FREE;
return 1;
}
TAILQ_REMOVE(&ppp_head, ppp, next);
dispose_PPPoE(ppp);
if (ppp->sid)
free(ppp->sid);
if (ppp->bundle)
CFRelease(ppp->bundle);
CFRelease(ppp->serviceID);
CFRelease(ppp->subtypeRef);
free(ppp);
return 0;
}
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 ppp *ppp, CFDictionaryRef service, CFDictionaryRef options, u_int8_t dialondemand)
{
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 = ppp->controlfd[WRITE];
writeparam(optfd, "[OPTIONS]");
if (gPluginsDir) {
CFStringGetCString(gPluginsDir, str, sizeof(str), kCFStringEncodingUTF8);
strcat(str, "PPPDialogs.ppp");
writestrparam(optfd, "plugin", str);
}
get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPVerboseLogging, options, service, &lval, 0);
if (lval)
writeparam(optfd, "debug");
ppp->flags &= ~(FLAG_ALERTERRORS + FLAG_ALERTPASSWORDS);
ppp_getoptval(ppp, options, service, PPP_OPT_ALERTENABLE, &lval, &len);
if (lval & PPP_ALERT_ERRORS)
ppp->flags |= FLAG_ALERTERRORS;
if (lval & PPP_ALERT_PASSWORDS)
ppp->flags |= FLAG_ALERTPASSWORDS;
if (ppp_getoptval(ppp, options, service, PPP_OPT_LOGFILE, sopt, &len) && sopt[0]) {
sprintf(str, "%s%s", sopt[0] == '/' ? "" : DIR_LOGS, sopt);
writestrparam(optfd, "logfile", str);
}
CFStringGetCString(ppp->subtypeRef, str2, sizeof(str2) - 4, kCFStringEncodingUTF8);
strcat(str2, ".ppp"); writestrparam(optfd, "plugin", str2);
if (ppp_getoptval(ppp, options, service, PPP_OPT_DEV_NAME, sopt, &len) && sopt[0])
writestrparam(optfd, "device", sopt);
if (ppp_getoptval(ppp, options, service, PPP_OPT_DEV_SPEED, &lval, &len) && lval) {
sprintf(str, "%d", lval);
writeparam(optfd, str);
}
switch (ppp->subtype) {
case PPP_TYPE_SERIAL:
get_str_option(ppp, kSCEntNetInterface, kSCPropNetInterfaceHardware, options, 0, sopt, &lval, "");
if (strcmp(sopt, "Modem")) {
break;
}
#if 1
modemdict = copyEntity(kSCDynamicStoreDomainSetup, ppp->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(ppp, 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(ppp, 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(ppp, kSCEntNetL2TP, CFSTR("UDPPort"), options, service, &lval, 0 );
writeintparam(optfd, "l2tpudpport", lval);
break;
case PPP_TYPE_PPTP:
get_int_option(ppp, kSCEntNetPPTP, CFSTR("TCPKeepAlive"), options, service, &lval, 0);
if (lval) {
get_int_option(ppp, kSCEntNetPPTP, CFSTR("TCPKeepAliveTimer"), options, service, &lval, 0);
}
else {
ppp_getoptval(ppp, options, service, PPP_OPT_LCP_ECHO, &lval, &len);
lval = lval >> 16;
}
writeintparam(optfd, "pptp-tcp-keepalive", lval);
break;
}
if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_TERMINALMODE, &lval, &len)) {
if (lval != PPP_COMM_TERM_NONE && ppp->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(ppp, options, service, PPP_OPT_COMM_TERMINALSCRIPT, sopt, &len) && sopt[0])
writestrparam(optfd, "terminalscript", sopt);
}
if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_REMOTEADDR, sopt, &len) && sopt[0])
writestrparam(optfd, "remoteaddress", sopt);
get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPCommRedialEnabled, options, service, &lval, 0);
if (lval) {
get_str_option(ppp, kSCEntNetPPP, kSCPropNetPPPCommAlternateRemoteAddress, options, service, sopt, &lval, "");
if (sopt[0])
writestrparam(optfd, "altremoteaddress", sopt);
get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPCommRedialCount, options, service, &lval, 0);
if (lval)
writeintparam(optfd, "redialcount", lval);
get_int_option(ppp, 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(ppp, options, service, PPP_OPT_COMM_IDLETIMER, &lval, &len) && lval) {
writeintparam(optfd, "idle", lval);
writeparam(optfd, "noidlerecv");
}
if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_SESSIONTIMER, &lval, &len) && lval)
writeintparam(optfd, "maxconnect", lval);
if (dialondemand) {
writeparam(optfd, "demand");
get_int_option(ppp, kSCEntNetPPP, CFSTR("HoldOffTime"), 0, service, &lval, 30);
writeintparam(optfd, "holdoff", lval);
if ((dialondemand & 0x2) && lval)
writeparam(optfd, "holdfirst");
get_int_option(ppp, kSCEntNetPPP, CFSTR("MaxFailure"), 0, service, &lval, 3);
writeintparam(optfd, "maxfail", lval);
}
if (ppp_getoptval(ppp, 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(ppp, options, service, PPP_OPT_LCP_HDRCOMP, &lval, &len)) {
if (!(lval & 1))
writeparam(optfd, "nopcomp");
if (!(lval & 2))
writeparam(optfd, "noaccomp");
}
if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_MRU, &lval, &len) && lval)
writeintparam(optfd, "mru", lval);
if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_MTU, &lval, &len) && lval)
writeintparam(optfd, "mtu", lval);
if (ppp_getoptval(ppp, 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(ppp, 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(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetIPv4)) {
writeparam(optfd, "noip");
}
else {
if (getStringFromEntity(kSCDynamicStoreDomainState, 0,
kSCEntNetIPv4, kSCPropNetIPv4Router, sopt, OPT_STR_LEN) && sopt[0])
writestrparam(optfd, "ipparam", sopt);
get_int_option(ppp, kSCEntNetIPv4, kSCPropNetOverridePrimary, 0 , service, &lval, 0);
if (lval)
writeparam(optfd, "defaultroute");
if (! (ppp_getoptval(ppp, options, service, PPP_OPT_IPCP_HDRCOMP, &lval, &len) && lval))
writeparam(optfd, "novj");
if (ppp->subtype == PPP_TYPE_L2TP || ppp->subtype == PPP_TYPE_PPTP ) {
writeintparam(optfd, "ip-src-address-filter", 2);
}
if (ppp_getoptval(ppp, 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(ppp, 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(ppp, kSCEntNetPPP, CFSTR("IPCPUsePeerDNS"), options, service, &lval, 1);
if (lval)
writeparam(optfd, "usepeerdns");
if (existEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetSMB)) {
get_int_option(ppp, kSCEntNetPPP, CFSTR("IPCPUsePeerWINS"), options, service, &lval, 1);
if (lval)
writeparam(optfd, "usepeerwins");
}
}
if (!existEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetIPv6)) {
}
else {
writeparam(optfd, "+ipv6");
writeparam(optfd, "ipv6cp-use-persistent");
}
get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPACSPEnabled, options, service, &lval, 0);
if (lval == 0)
writeparam(optfd, "noacsp");
get_int_option(ppp, kSCEntNetPPP, CFSTR("UseDHCP"), options, service, &lval, (ppp->subtype == PPP_TYPE_L2TP || ppp->subtype == PPP_TYPE_PPTP) ? 1 : 0);
if (lval == 1)
writeparam(optfd, "use-dhcp");
writeparam(optfd, "noauth");
if (ppp_getoptval(ppp, options, service, PPP_OPT_AUTH_PROTO, &lval, &len) && (lval != PPP_AUTH_NONE)) {
CFStringRef encryption = NULL;
if (ppp_getoptval(ppp, options, service, PPP_OPT_AUTH_NAME, sopt, &len) && sopt[0]) {
writestrparam(optfd, "user", sopt);
needpasswd = 1;
lval1 = get_str_option(ppp, 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
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(ppp, 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; }
}
}
}
}
get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPCCPEnabled, options, service, &lval, 0);
if (lval
&& ppp_getoptval(ppp, options, service, PPP_OPT_AUTH_PROTO, &lval, &len)
&& (lval == OPT_AUTH_PROTO_DEF)) {
writeparam(optfd, "mppe-stateless");
get_int_option(ppp, kSCEntNetPPP, CFSTR("CCPMPPE128Enabled"), options, service, &lval, 1);
writeparam(optfd, lval ? "mppe-128" : "nomppe-128");
get_int_option(ppp, 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 (!(ppp->flags & FLAG_ALERTPASSWORDS) || !needpasswd)
writeparam(optfd, "noaskpassword");
get_str_option(ppp, 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(ppp, kSCEntNetPPP, kSCPropNetPPPIdleReminder, options, service, &lval, 0);
if (lval) {
get_int_option(ppp, 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);
}
}
}
get_str_option(ppp, kSCEntNetPPP, kSCPropUserDefinedName, options, service, sopt, &lval, "");
if (sopt[0])
writestrparam(optfd, "call", sopt);
writeparam(optfd, "[EOP]");
return 0;
}
static
int change_pppd_params(struct ppp *ppp, 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 = ppp->controlfd[WRITE];
writeparam(optfd, "[OPTIONS]");
get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPIdleReminder, options, service, &lval, 0);
if (lval)
get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPIdleReminderTimer, options, service, &lval, 0);
writeintparam(optfd, "reminder", lval);
ppp_getoptval(ppp, 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_connect(struct ppp *ppp, CFDictionaryRef options, u_int8_t dialondemand, void *client, int autoclose, uid_t uid, gid_t gid, mach_port_t bootstrap)
{
#define MAXARG 10
char *cmdarg[MAXARG];
u_int32_t lval, i, argi = 0;
CFDictionaryRef service;
CFStringRef autodial;
if (options) {
int willdial = 0, autodialval = 0;
autodial = CFDictionaryGetValue(options, kSCPropNetPPPOnDemandPriority);
if (!autodial) {
willdial = 1;
}
else if (CFGetTypeID(autodial) == CFStringGetTypeID()) {
if (gLoggedInUser && uid == gLoggedInUserUID) {
if (CFStringCompare(autodial, kSCValNetPPPOnDemandPriorityHigh, 0) == kCFCompareEqualTo)
autodialval = 1;
else if (CFStringCompare(autodial, kSCValNetPPPOnDemandPriorityLow, 0) == kCFCompareEqualTo)
autodialval = 2;
else if (CFStringCompare(autodial, kSCValNetPPPOnDemandPriorityDefault, 0) == kCFCompareEqualTo) {
CFDictionaryRef dict;
if (dict = CFDictionaryGetValue(options, kSCEntNetPPP)) {
CFStringRef str;
str = CFDictionaryGetValue(dict, kSCPropNetPPPOnDemandMode);
if (str && CFGetTypeID(str) == CFStringGetTypeID()) {
if (CFStringCompare(str, kSCValNetPPPOnDemandModeAggressive, 0) == kCFCompareEqualTo)
autodialval = 1;
else if (CFStringCompare(str, kSCValNetPPPOnDemandModeConservative, 0) == kCFCompareEqualTo)
autodialval = 2;
else
autodialval = 0;
}
}
}
}
switch (autodialval) {
case 1: willdial = 1;
break;
case 2: if (ppp->flags & FLAG_FIRSTDIAL) {
willdial = 1;
}
break;
default:
break;
}
}
if (!willdial)
return EIO; }
if (gStopRls ||
(gSleeping
&& (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnSleep, &lval)
|| lval)))
return EIO;
ppp->flags &= ~FLAG_CONFIGCHANGEDNOW;
ppp->flags &= ~FLAG_CONFIGCHANGEDLATER;
ppp->flags &= ~FLAG_FIRSTDIAL;
switch (ppp->phase) {
case PPP_IDLE:
break;
case PPP_DORMANT: case PPP_HOLDOFF:
my_CFRelease(ppp->newconnectopts);
ppp->newconnectopts = options;
ppp->newconnectuid = uid;
ppp->newconnectuid = gid;
ppp->newconnectbootstrap = bootstrap;
my_CFRetain(ppp->newconnectopts);
ppp_disconnect(ppp, 0, SIGTERM);
ppp->flags |= FLAG_CONNECT;
if (client)
add_client(ppp, client, autoclose);
return 0;
default:
if (client) {
if (my_CFEqual(options, ppp->connectopts)) {
add_client(ppp, client, autoclose);
return 0;
}
}
return EIO; }
ppp->laststatus = EXIT_FATAL_ERROR;
ppp->lastdevstatus = 0;
service = copyService(kSCDynamicStoreDomainSetup, ppp->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, ppp->sid);
addparam(cmdarg, &argi, "controlled");
if ((socketpair(AF_LOCAL, SOCK_STREAM, 0, ppp->controlfd) == -1)
|| (socketpair(AF_LOCAL, SOCK_STREAM, 0, ppp->statusfd) == -1))
goto end;
ppp->pid = _SCDPluginExecCommand2(exec_callback, (void*)ppp_makeref(ppp), uid, gid, PATH_PPPD, cmdarg, exec_postfork, (void*)ppp_makeref(ppp));
if (ppp->pid == -1)
goto end;
if (send_pppd_params(ppp, service, options, dialondemand) == -1) {
kill(ppp->pid, SIGTERM);
goto end;
}
ppp_socket_create_client(ppp->statusfd[READ], 1, 0, 0);
if (client)
add_client(ppp, client, autoclose);
ppp->laststatus = EXIT_OK;
ppp_updatephase(ppp, PPP_INITIALIZE);
if (dialondemand)
ppp->flags |= FLAG_DIALONDEMAND;
else
ppp->flags &= ~FLAG_DIALONDEMAND;
ppp->connectopts = options;
my_CFRetain(ppp->connectopts);
ppp->uid = uid;
ppp->gid = gid;
ppp->bootstrap = bootstrap;
end:
if (service)
CFRelease(service);
for (i = 0; i < argi; i++)
free(cmdarg[i]);
if (ppp->pid == -1) {
my_close(ppp->statusfd[READ]);
ppp->statusfd[READ] = -1;
my_close(ppp->statusfd[WRITE]);
ppp->statusfd[WRITE] = -1;
my_close(ppp->controlfd[READ]);
ppp->controlfd[READ] = -1;
my_close(ppp->controlfd[WRITE]);
ppp->controlfd[WRITE] = -1;
display_error(ppp);
}
return ppp->laststatus;
}
static void
exec_postfork(pid_t pid, void *arg)
{
struct ppp *ppp = ppp_findbyref((u_int32_t)arg);
if (pid) {
int yes = 1;
my_close(ppp->controlfd[READ]);
ppp->controlfd[READ] = -1;
my_close(ppp->statusfd[WRITE]);
ppp->statusfd[WRITE] = -1;
if (ioctl(ppp->controlfd[WRITE], FIONBIO, &yes) == -1) {
}
} else {
int i;
setup_bootstrap_port();
my_close(ppp->controlfd[WRITE]);
ppp->controlfd[WRITE] = -1;
my_close(ppp->statusfd[READ]);
ppp->statusfd[READ] = -1;
if (ppp->controlfd[READ] != STDIN_FILENO) {
dup2(ppp->controlfd[READ], STDIN_FILENO);
}
if (ppp->statusfd[WRITE] != STDOUT_FILENO) {
dup2(ppp->statusfd[WRITE], STDOUT_FILENO);
}
close(STDERR_FILENO);
open(_PATH_DEVNULL, O_RDWR, 0);
for (i = getdtablesize() - 1; i > STDERR_FILENO; i--) close(i);
}
return;
}
static
void exec_callback(pid_t pid, int status, struct rusage *rusage, void *context)
{
struct ppp *ppp = ppp_findbyref((u_int32_t)context);
u_int32_t lval, failed = 0;
int exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
if (exitcode < 0) {
}
else if (exitcode > EXIT_PEER_NOT_AUTHORIZED) {
ppp_updatestatus(ppp, 127, 0);
}
else if (ppp->phase == PPP_INITIALIZE) {
failed = 1;
ppp_updatestatus(ppp, exitcode, 0);
}
ppp_updatephase(ppp, PPP_IDLE);
ppp->statusfd[READ] = -1;
my_close(ppp->controlfd[WRITE]);
ppp->controlfd[WRITE] = -1;
my_CFRelease(ppp->connectopts);
ppp->connectopts = 0;
if (ppp->flags & FLAG_FREE) {
ppp->flags &= ~FLAG_FREE;
dispose_service(ppp);
ppp = 0;
}
if (gSleeping && !will_sleep(1)) {
if (gSleepNotification) {
CFUserNotificationCancel(gSleepNotification);
CFRelease(gSleepNotification);
gSleepNotification = 0;
}
IOAllowPowerChange(gIOPort, gSleepArgument);
}
if (ppp == 0)
return;
if (gStopRls && is_idle()) {
CFRunLoopSourceSignal(gStopRls);
return;
}
if (ppp->flags & FLAG_CONNECT) {
ppp_connect(ppp, ppp->newconnectopts, 0, 0, 0, ppp->newconnectuid, ppp->newconnectgid, ppp->newconnectbootstrap);
my_CFRelease(ppp->newconnectopts);
ppp->newconnectopts = 0;
ppp->newconnectuid = 0;
ppp->newconnectgid = 0;
ppp->newconnectbootstrap = 0;
ppp->flags &= ~FLAG_CONNECT;
}
else {
if (failed == 0
&& ((ppp->flags & (FLAG_CONFIGCHANGEDNOW + FLAG_CONFIGCHANGEDLATER)) || !(ppp->flags & FLAG_DIALONDEMAND))
&& (getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &lval) && lval)
&& (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &lval) || !lval
|| gLoggedInUser)) {
ppp_connect(ppp, 0, ppp->flags & FLAG_CONFIGCHANGEDNOW ? 1 : 3, 0, 0, 0, 0, 0);
}
}
}
int ppp_disconnect(struct ppp *ppp, void *client, int signal)
{
if (client) {
if (get_client(ppp, client))
remove_client(ppp, client);
if (TAILQ_FIRST(&ppp->client_head))
return 0;
}
else {
remove_all_clients(ppp);
}
if (ppp->flags & (FLAG_CONFIGCHANGEDNOW + FLAG_CONFIGCHANGEDLATER))
signal = SIGTERM;
switch (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:
ppp->flags &= ~FLAG_CONNECT;
case PPP_CONNECTLINK:
ppp_updatephase(ppp, PPP_DISCONNECTLINK);
break;
default:
ppp_updatephase(ppp, PPP_TERMINATE);
}
kill(ppp->pid, signal);
return 0;
}
int ppp_suspend(struct ppp *ppp)
{
if (ppp->phase != PPP_IDLE)
kill(ppp->pid, SIGTSTP);
return 0;
}
int ppp_resume(struct ppp *ppp)
{
if (ppp->phase != PPP_IDLE)
kill(ppp->pid, SIGCONT);
return 0;
}
void ppp_updatestatus(struct ppp *ppp, int status, int devstatus)
{
ppp->laststatus = status;
ppp->lastdevstatus = devstatus;
display_error(ppp);
}
void ppp_updatephase(struct ppp *ppp, int phase)
{
if (ppp->statusfd[READ] == -1)
return;
if (phase == ppp->phase)
return;
ppp->phase = phase;
client_notify(ppp->serviceID, ppp->sid, ppp_makeref(ppp), ppp->phase, 0, CLIENT_FLAG_NOTIFY_STATUS);
switch (ppp->phase) {
case PPP_RUNNING:
ppp->ifname[0] = 0;
getStringFromEntity(kSCDynamicStoreDomainState, ppp->serviceID,
kSCEntNetPPP, kSCPropInterfaceName, ppp->ifname, sizeof(ppp->ifname));
break;
case PPP_DORMANT:
ppp->ifname[0] = 0;
getStringFromEntity(kSCDynamicStoreDomainState, ppp->serviceID,
kSCEntNetPPP, kSCPropInterfaceName, ppp->ifname, sizeof(ppp->ifname));
case PPP_HOLDOFF:
if (ppp->flags & FLAG_CONFIGCHANGEDLATER)
ppp_disconnect(ppp, 0, SIGTERM);
break;
}
}
int ppp_getstatus(struct ppp *ppp, 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 (ppp->phase) {
case PPP_DORMANT:
case PPP_HOLDOFF:
stat->status = PPP_IDLE; break;
default:
stat->status = 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, 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(kSCDynamicStoreDomainState, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPConnectTime, &conntime);
disconntime = 0;
getNumberFromEntity(kSCDynamicStoreDomainState, ppp->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(kSCDynamicStoreDomainState, ppp->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(ppp->subtype, ppp->laststatus, ppp->lastdevstatus);
}
*replylen = sizeof(struct ppp_status);
return 0;
}
int ppp_copyextendedstatus(struct ppp *ppp, 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, ppp->phase);
if (ppp->phase != PPP_IDLE)
AddStringFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPCommRemoteAddress, dict);
switch (ppp->phase) {
case PPP_RUNNING:
case PPP_ONHOLD:
AddNumberFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPConnectTime, dict);
AddNumberFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDisconnectTime, dict);
AddNumberFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPCompressionPField, dict);
AddNumberFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPCompressionACField, dict);
AddNumberFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPMRU, dict);
AddNumberFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPMTU, dict);
AddNumberFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPReceiveACCM, dict);
AddNumberFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPTransmitACCM, dict);
AddNumberFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPIPCPCompressionVJ, dict);
break;
case PPP_WAITONBUSY:
AddNumberFromState(ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPRetryConnectTime, dict);
break;
case PPP_DORMANT:
break;
default:
AddNumber(dict, kSCPropNetPPPLastCause, ppp->laststatus);
AddNumber(dict, kSCPropNetPPPDeviceLastCause, ppp->lastdevstatus);
}
CFDictionaryAddValue(statusdict, kSCEntNetPPP, dict);
CFRelease(dict);
if (ppp->subtype == PPP_TYPE_SERIAL
&& (ppp->phase == PPP_RUNNING || ppp->phase == PPP_ONHOLD)) {
if ((dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0)
goto fail;
AddNumberFromState(ppp->serviceID, kSCEntNetModem, kSCPropNetModemConnectSpeed, dict);
CFDictionaryAddValue(statusdict, kSCEntNetModem, dict);
CFRelease(dict);
}
if (ppp->phase == PPP_RUNNING || ppp->phase == PPP_ONHOLD) {
dict = (CFMutableDictionaryRef)copyEntity(kSCDynamicStoreDomainState, ppp->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 ppp *ppp, 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 (ppp->phase != PPP_RUNNING
&& 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, 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 ppp *ppp, 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(kSCDynamicStoreDomainSetup, ppp->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 ppp *ppp, 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 = ppp->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;
}
int ppp_clientgone(void *client)
{
struct ppp *ppp;
struct ppp_client *pppclient;
TAILQ_FOREACH(ppp, &ppp_head, next) {
pppclient = get_client(ppp, client);
if (pppclient && pppclient->autoclose) {
ppp_disconnect(ppp, client, SIGTERM);
}
}
return 0;
}
static
int add_client(struct ppp *ppp, void *client, int autoclose)
{
struct ppp_client *pppclient;
pppclient = malloc(sizeof(struct ppp_client));
if (pppclient == 0)
return -1;
pppclient->client = client;
pppclient->autoclose = autoclose;
TAILQ_INSERT_TAIL(&ppp->client_head, pppclient, next);
return 0;
}
static
int remove_client(struct ppp *ppp, void *client)
{
struct ppp_client *pppclient;
TAILQ_FOREACH(pppclient, &ppp->client_head, next) {
if (pppclient->client == client) {
TAILQ_REMOVE(&ppp->client_head, pppclient, next);
free(pppclient);
return 0;
}
}
return 0;
}
static
struct ppp_client *get_client(struct ppp *ppp, void *client)
{
struct ppp_client *pppclient;
TAILQ_FOREACH(pppclient, &ppp->client_head, next)
if (pppclient->client == client)
return pppclient;
return 0;
}
static
int remove_all_clients(struct ppp *ppp)
{
struct ppp_client *pppclient;
while (pppclient = TAILQ_FIRST(&ppp->client_head)) {
TAILQ_REMOVE(&ppp->client_head, pppclient, next);
free(pppclient);
}
return 0;
}