#include <string.h>
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <paths.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/route.h>
#include <net/dlil.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/IOMessage.h>
#include <mach/mach_time.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCDPlugin.h>
#define SCLog
#include "ppp_msg.h"
#include "ppp_privmsg.h"
#include "../Family/ppp_domain.h"
#include "../Helpers/pppd/pppd.h"
#include "ppp_client.h"
#include "ppp_command.h"
#include "ppp_manager.h"
#include "ppp_option.h"
enum {
READ = 0, WRITE = 1 };
TAILQ_HEAD(, ppp) ppp_head;
io_connect_t gIOPort;
io_connect_t gSleeping;
long gSleepArgument;
CFUserNotificationRef gSleepNotification;
double gTimeScaleSeconds;
static void ppp_display_error(struct ppp *ppp);
static void ppp_sleep(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);
u_int32_t ppp_translate_error(u_int16_t subtype, u_int32_t native_ppp_error, u_int32_t native_dev_error);
int get_str (struct ppp *ppp, CFDictionaryRef optsdict,
CFStringRef entity, CFStringRef property,
u_char *opt, u_int32_t *outlen, CFDictionaryRef setupdict, u_char lookinsetup, u_char *defaultval);
int ppp_addclient(struct ppp *ppp, void *client, int autoclose);
int ppp_delclient(struct ppp *ppp, void *client);
int ppp_delclients(struct ppp *ppp);
struct ppp_client *ppp_getclient(struct ppp *ppp, void *client);
u_long ppp_init_all() {
IONotificationPortRef notify;
io_object_t iterator;
mach_timebase_info_data_t timebaseInfo;
gSleeping = 0; gSleepNotification = 0;
TAILQ_INIT(&ppp_head);
gIOPort = IORegisterForSystemPower(0, ¬ify, ppp_sleep, &iterator);
if (gIOPort == 0) {
printf("IORegisterForSystemPower failed\n");
return 1;
}
if (mach_timebase_info(&timebaseInfo) != KERN_SUCCESS) { printf("mach_timebase_info failed\n");
return 1;
}
gTimeScaleSeconds = ((double) timebaseInfo.numer / (double) timebaseInfo.denom) / 1000000000;
CFRunLoopAddSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(notify),
kCFRunLoopDefaultMode);
if (options_init_all()) {
return 1;
}
return 0;
}
void ppp_dispose_all()
{
}
u_int32_t ppp_makeref(struct ppp *ppp)
{
return (((u_long)ppp->subtype) << 16) + ppp->unit;
}
u_int32_t ppp_makeifref(struct ppp *ppp)
{
return (((u_long)ppp->subtype) << 16) + ppp->ifunit;
}
struct ppp *ppp_findbyname(u_char *name, u_short ifunit)
{
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if ((ppp->ifunit == ifunit)
&& !strncmp(ppp->name, name, IFNAMSIZ)) {
return ppp;
}
}
return 0;
}
struct ppp *ppp_findbyserviceID(CFStringRef serviceID)
{
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next)
if (CFEqual(ppp->serviceID, serviceID))
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;
}
void ppp_setorder(struct ppp *ppp, u_int16_t order)
{
TAILQ_REMOVE(&ppp_head, ppp, next);
switch (order) {
case 0:
TAILQ_INSERT_HEAD(&ppp_head, ppp, next);
break;
case 0xFFFF:
TAILQ_INSERT_TAIL(&ppp_head, ppp, next);
break;
}
}
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;
}
void ppp_setphase(struct ppp *ppp, int phase)
{
ppp->phase = phase;
client_notify(ppp->sid, ppp_makeref(ppp), ppp->phase, 0, 2);
}
void ppp_printlist()
{
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);
}
}
struct ppp *ppp_findbyifref(u_long ref)
{
u_short subtype = ref >> 16;
u_short ifunit = ref & 0xFFFF;
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (((ppp->subtype == subtype) || (subtype == 0xFFFF))
&& ((ppp->ifunit == ifunit) || (ifunit == 0xFFFF))) {
return ppp;
}
}
return 0;
}
u_short ppp_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;
}
struct ppp *ppp_new(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 = ppp_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->ifunit = 0xFFFF; ppp->pid = -1;
strncpy(ppp->name, "ppp", IFNAMSIZ);
ppp->subtype = subtype;
ppp_setphase(ppp, PPP_IDLE);
ppp->alertenable = OPT_ALERT_DEF;
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;
}
void ppp_updatestate(struct ppp *ppp, CFDictionaryRef service)
{
u_int32_t lval;
int val;
u_char str[16];
struct ppp *ppp2;
if (getNumber(service, kSCPropNetPPPStatus, &lval)) {
if (ppp->started_link == 0 || lval) {
ppp->started_link = 0;
ppp_setphase(ppp, lval);
ppp->ifunit = 0xFFFF;
if (ppp->phase == PPP_RUNNING || ppp->phase == PPP_ONHOLD || ppp->phase == PPP_STATERESERVED) {
if (getString(service, kSCPropInterfaceName, str, sizeof(str))) {
sscanf(str, "ppp%d", &val);
ppp->ifunit = val;
}
}
}
}
if (ppp->kill_link) {
val = ppp->kill_link;
ppp->kill_link = 0;
ppp_dodisconnect(ppp, val, 0);
}
if (ppp->needdispose && !ppp_dispose(ppp))
return;
if (!getNumber(service, kSCPropNetPPPDeviceLastCause, &ppp->lastdevstatus))
ppp->lastdevstatus = 0;
if (!getNumber(service, kSCPropNetPPPLastCause, &ppp->laststatus))
ppp->laststatus = 0;
if (!getNumber(service, kSCPropNetPPPConnectTime, &ppp->conntime))
ppp->conntime = 0;
if (!getNumber(service, kSCPropNetPPPDisconnectTime, &ppp->disconntime))
ppp->disconntime = 0;
if (ppp->phase != ppp->oldphase) {
switch (ppp->phase) {
case PPP_STATERESERVED:
case PPP_HOLDOFF:
if (ppp->oldphase != PPP_INITIALIZE && ppp->oldphase != PPP_HOLDOFF)
ppp_display_error(ppp);
if (ppp->setupchanged)
ppp_dodisconnect(ppp, 15, 0);
break;
case PPP_IDLE:
ppp->kill_sent = 0;
if (ppp->oldphase != PPP_STATERESERVED && ppp->oldphase != PPP_HOLDOFF)
ppp_display_error(ppp);
if (gSleeping) {
TAILQ_FOREACH(ppp2, &ppp_head, next)
if (ppp2->phase != PPP_IDLE)
break;
if (ppp2 == 0) {
if (gSleepNotification) {
CFUserNotificationCancel(gSleepNotification);
CFRelease(gSleepNotification);
gSleepNotification = 0;
}
IOAllowPowerChange(gIOPort, gSleepArgument);
}
}
if (ppp->connectopts) {
CFRelease(ppp->connectopts);
ppp->connectopts = 0;
}
if (ppp->needconnect) {
ppp->needconnect = 0;
ppp_doconnect(ppp, ppp->needconnectopts, 0, 0, 0);
if (ppp->needconnectopts)
CFRelease(ppp->needconnectopts);
ppp->needconnectopts = 0;
}
else {
if ((ppp->setupchanged || ((ppp->dialondemand == 0) && (ppp->laststatus != EXIT_USER_REQUEST)))
&& (getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &lval) && lval)
&& (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &lval) || !lval
|| gLoggedInUser)) {
ppp_doconnect(ppp, 0, 1, 0, 0);
}
}
break;
}
ppp->oldphase = ppp->phase;
}
return;
}
CFStringRef ppp_copyUserLocalizedString(CFBundleRef bundle,
CFStringRef key, CFStringRef value, CFArrayRef userLanguages)
{
CFStringRef result = NULL, errStr= NULL;
CFDictionaryRef stringTable;
CFDataRef tableData;
SInt32 errCode;
CFURLRef tableURL;
CFArrayRef locArray, prefArray;
if (userLanguages == NULL)
return CFBundleCopyLocalizedString(bundle, key, value, NULL);
if (key == NULL)
return (value ? CFRetain(value) : CFRetain(CFSTR("")));
locArray = CFBundleCopyBundleLocalizations(bundle);
if (locArray) {
prefArray = CFBundleCopyLocalizationsForPreferences(locArray, userLanguages);
if (prefArray) {
if (CFArrayGetCount(prefArray)) {
tableURL = CFBundleCopyResourceURLForLocalization(bundle, CFSTR("Localizable"), CFSTR("strings"), NULL,
CFArrayGetValueAtIndex(prefArray, 0));
if (tableURL) {
if (CFURLCreateDataAndPropertiesFromResource(NULL, tableURL, &tableData, NULL, NULL, &errCode)) {
stringTable = CFPropertyListCreateFromXMLData(NULL, tableData, kCFPropertyListImmutable, &errStr);
if (errStr)
CFRelease(errStr);
if (stringTable) {
result = CFDictionaryGetValue(stringTable, key);
if (result)
CFRetain(result);
CFRelease(stringTable);
}
CFRelease(tableData);
}
CFRelease(tableURL);
}
}
CFRelease(prefArray);
}
CFRelease(locArray);
}
if (result == NULL)
result = (value && !CFEqual(value, CFSTR(""))) ? CFRetain(value) : CFRetain(key);
return result;
}
void ppp_display_error(struct ppp *ppp)
{
CFStringRef ppp_msg = NULL, dev_msg = NULL, msg = NULL;
CFPropertyListRef langRef = NULL;
if ((ppp->alertenable & PPP_ALERT_ERRORS) == 0)
return;
if (gLoggedInUser) {
CFPreferencesSynchronize(kCFPreferencesAnyApplication,
gLoggedInUser, kCFPreferencesAnyHost);
langRef = CFPreferencesCopyValue(CFSTR("AppleLanguages"),
kCFPreferencesAnyApplication, gLoggedInUser, kCFPreferencesAnyHost);
}
if (ppp->lastdevstatus && ppp->bundle) {
dev_msg = CFStringCreateWithFormat(0, 0, CFSTR("Device Error %d"), ppp->lastdevstatus);
if (dev_msg)
msg = ppp_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 = ppp_copyUserLocalizedString(gBundleRef, ppp_msg, ppp_msg, langRef) ;
}
if (msg && CFStringGetLength(msg))
CFUserNotificationDisplayNotice(120, kCFUserNotificationNoteAlertLevel,
gIconURLRef, NULL, gBundleURLRef, CFSTR("Internet Connect"), msg, NULL);
if (msg) CFRelease(msg);
if (ppp_msg) CFRelease(ppp_msg);
if (dev_msg) CFRelease(dev_msg);
if (langRef) CFRelease(langRef);
}
void ppp_updatesetup(struct ppp *ppp, CFDictionaryRef service)
{
ppp->dosetup = 1;
return;
}
void ppp_postupdatesetup()
{
u_int32_t lval;
struct ppp *ppp;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (ppp->dosetup) {
ppp->dosetup = 0;
ppp->needdispose = 0;
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_doconnect(ppp, 0, 1, 0, 0);
}
break;
case PPP_STATERESERVED:
case PPP_HOLDOFF:
ppp->setupchanged = 1;
ppp_dodisconnect(ppp, 15, 0);
break;
default :
ppp->setupchanged = 1;
break;
}
}
}
return;
}
void ppp_sleep(void * x, io_service_t y, natural_t messageType, void *messageArgument)
{
CFMutableDictionaryRef dict;
SInt32 error;
u_int32_t disc, allow, alerte, lval;
CFDictionaryRef service = NULL;
struct ppp *ppp;
switch ( messageType ) {
case kIOMessageSystemWillSleep:
gSleeping = 1; allow = 1;
alerte = 0;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (ppp->phase != PPP_IDLE
&& (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnSleep, &disc)
|| disc
|| ppp->phase == PPP_STATERESERVED
|| ppp->phase == PPP_HOLDOFF)) {
allow = 0;
if (ppp->phase != PPP_STATERESERVED && ppp->phase != PPP_HOLDOFF)
alerte = 1;
ppp_dodisconnect(ppp, 15, 0);
}
}
if (allow) {
IOAllowPowerChange(gIOPort, (long)messageArgument);
break;
}
gSleepArgument = (long)messageArgument;
if (alerte) {
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);
}
}
break;
case kIOMessageCanSystemSleep:
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (ppp->phase != PPP_IDLE
&& ppp->phase != PPP_STATERESERVED
&& ppp->phase != PPP_HOLDOFF) {
IOCancelPowerChange(gIOPort, (long)messageArgument);
return;
}
}
IOAllowPowerChange(gIOPort, (long)messageArgument);
break;
case kIOMessageSystemWillNotSleep:
break;
case kIOMessageSystemHasPoweredOn:
gSleeping = 0; if (gSleepNotification) {
CFUserNotificationCancel(gSleepNotification);
CFRelease(gSleepNotification);
gSleepNotification = 0;
}
TAILQ_FOREACH(ppp, &ppp_head, next) {
service = copyEntity(kSCDynamicStoreDomainState, ppp->serviceID, kSCEntNetPPP);
if (service) {
ppp_updatestate(ppp, service);
CFRelease(service);
if (ppp->phase == PPP_IDLE) {
if ((getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &lval) && lval)
&& (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &lval) || !lval
|| gLoggedInUser)) {
ppp_doconnect(ppp, 0, 1, 0, 0);
}
}
}
}
break;
}
}
int ppp_logout()
{
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_dodisconnect(ppp, 15, 0);
}
return 0;
}
int ppp_login()
{
struct ppp *ppp;
u_int32_t val;
TAILQ_FOREACH(ppp, &ppp_head, next) {
if (ppp->phase == PPP_IDLE
&& getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &val)
&& val)
ppp_doconnect(ppp, 0, 1, 0, 0);
}
return 0;
}
int ppp_logswitch()
{
struct ppp *ppp;
u_int32_t disc, val, demand;
TAILQ_FOREACH(ppp, &ppp_head, next) {
demand = getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &val) && val;
switch (ppp->phase) {
case PPP_IDLE:
if (demand)
ppp_doconnect(ppp, 0, 1, 0, 0);
break;
default:
if (getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &disc)
&& disc) {
ppp->setupchanged = demand;
ppp_dodisconnect(ppp, 15, 0);
}
}
}
return 0;
}
int ppp_dispose(struct ppp *ppp)
{
ppp_dodisconnect(ppp, 15, 0);
if (ppp->phase != PPP_IDLE) {
ppp->needdispose = 1;
return 1;
}
TAILQ_REMOVE(&ppp_head, ppp, next);
if (ppp->sid)
free(ppp->sid);
if (ppp->bundle)
CFRelease(ppp->bundle);
CFRelease(ppp->serviceID);
CFRelease(ppp->subtypeRef);
free(ppp);
return 0;
}
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)++;
}
}
void addintparam(char **arg, u_int32_t *argi, char *param, u_int32_t val)
{
u_char str[32];
addparam(arg, argi, param);
sprintf(str, "%d", val);
addparam(arg, argi, str);
}
void addstrparam(char **arg, u_int32_t *argi, char *param, char *val)
{
addparam(arg, argi, param);
addparam(arg, argi, val);
}
int ppp_triggerdemand(struct ppp *ppp)
{
struct ifreq ifr;
int s, i;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
return errno;
memset (&ifr, 0, sizeof (ifr));
sprintf(ifr.ifr_name, "ppp%d", ppp->ifunit);
if (ioctl(s, OSIOCGIFDSTADDR, &ifr) < 0) {
close(s);
return errno;
}
((struct sockaddr_in *)&ifr.ifr_addr)->sin_port = 1;
i = sendto(s, &s, 1, 0, (struct sockaddr *)&ifr.ifr_addr, sizeof(struct sockaddr_in));
close(s);
return 0;
}
void escape_str(char *dst, int maxlen, char *src)
{
while (*src && (maxlen > 1)) {
switch (*src) {
case '\\':
case '\"':
case '\'':
case ' ':
*dst++ = '\\';
maxlen--;
}
*dst++ = *src++;
maxlen--;
}
*dst = 0;
}
int ppp_doconnect(struct ppp *ppp, CFDictionaryRef options, u_int8_t dialondemand, void *client, int autoclose)
{
#define MAXARG 100
char str[MAXPATHLEN], str2[256], *cmdarg[MAXARG];
int needpasswd = 0, auth_default = 1, from_service;
u_int32_t auth_bits = 0xF;
u_char sopt[OPT_STR_LEN];
u_char pipearg[MAXPATHLEN];
u_int32_t len, lval, lval1, i, argi = 0;
CFDictionaryRef service = NULL, pppdict = NULL, dict;
CFArrayRef array = NULL;
CFStringRef string = NULL;
if (gSleeping)
return EIO;
ppp->setupchanged = 0;
switch (ppp->phase) {
case PPP_IDLE:
break;
case PPP_STATERESERVED: case PPP_HOLDOFF:
ppp->needconnectopts = options;
if (options)
CFRetain(options);
ppp_dodisconnect(ppp, 15, 0);
ppp->needconnect = 1;
if (client)
ppp_addclient(ppp, client, autoclose);
ppp_setphase(ppp, PPP_INITIALIZE);
return 0;
break;
default:
if (client) {
if ((ppp->needconnect && CFEqual(options, ppp->needconnectopts))
|| (!ppp->needconnect && CFEqual(options, ppp->connectopts))) {
ppp_addclient(ppp, client, autoclose);
return 0;
}
}
return EIO; }
service = copyService(kSCDynamicStoreDomainSetup, ppp->serviceID);
if (!service)
return EIO;
pppdict = CFDictionaryGetValue(service, kSCEntNetPPP);
if ((pppdict == 0) || (CFGetTypeID(pppdict) != CFDictionaryGetTypeID())) {
CFRelease(service);
return EIO; }
pipearg[0] = 0;
argi = 0;
for (i = 0; i < MAXARG; i++) {
cmdarg[i] = 0;
}
addparam(cmdarg, &argi, PPPD_PRGM);
addstrparam(cmdarg, &argi, "serviceid", ppp->sid);
addintparam(cmdarg, &argi, "optionsfd", STDIN_FILENO);
if (gPluginsDir) {
CFStringGetCString(gPluginsDir, str, sizeof(str), kCFStringEncodingUTF8);
strcat(str, "PPPDialogs.ppp");
addstrparam(cmdarg, &argi, "plugin", str);
}
get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPVerboseLogging, options, service, &lval, 0);
if (lval)
addparam(cmdarg, &argi, "debug");
ppp_getoptval(ppp, options, service, PPP_OPT_ALERTENABLE, &ppp->alertenable, &len);
if (ppp_getoptval(ppp, options, service, PPP_OPT_LOGFILE, sopt, &len) && sopt[0]) {
sprintf(str, "%s%s", sopt[0] == '/' ? "" : DIR_LOGS, sopt);
addstrparam(cmdarg, &argi, "logfile", str);
}
CFStringGetCString(ppp->subtypeRef, str2, sizeof(str2) - 4, kCFStringEncodingUTF8);
strcat(str2, ".ppp"); addstrparam(cmdarg, &argi, "plugin", str2);
if (ppp_getoptval(ppp, options, service, PPP_OPT_DEV_NAME, sopt, &len) && sopt[0])
addstrparam(cmdarg, &argi, "device", sopt);
if (ppp_getoptval(ppp, options, service, PPP_OPT_DEV_SPEED, &lval, &len) && lval) {
sprintf(str, "%d", lval);
addparam(cmdarg, &argi, str);
}
switch (ppp->subtype) {
case PPP_TYPE_SERIAL:
get_str_option(ppp, kSCEntNetInterface, kSCPropNetInterfaceHardware, options, 0, sopt, &lval, "");
if (strcmp(sopt, "Modem")) {
break;
}
if (ppp_getoptval(ppp, options, 0, PPP_OPT_DEV_CONNECTSCRIPT, sopt, &len) && sopt[0]) {
addstrparam(cmdarg, &argi, "modemscript", sopt);
get_int_option(ppp, kSCEntNetModem, kSCPropNetModemSpeaker, options, 0, &lval, 1);
addparam(cmdarg, &argi, lval ? "modemsound" : "nomodemsound");
get_int_option(ppp, kSCEntNetModem, kSCPropNetModemErrorCorrection, options, 0, &lval, 1);
addparam(cmdarg, &argi, lval ? "modemreliable" : "nomodemreliable");
get_int_option(ppp, kSCEntNetModem, kSCPropNetModemDataCompression, options, 0, &lval, 1);
addparam(cmdarg, &argi, lval ? "modemcompress" : "nomodemcompress");
get_int_option(ppp, kSCEntNetModem, kSCPropNetModemPulseDial, options, 0, &lval, 0);
addparam(cmdarg, &argi, lval ? "modempulse" : "modemtone");
lval = 0;
ppp_getoptval(ppp, options, 0, PPP_OPT_DEV_DIALMODE, &lval, &len);
addintparam(cmdarg, &argi, "modemdialmode", lval);
}
break;
case PPP_TYPE_L2TP:
string = get_cf_option(kSCEntNetL2TP, kSCPropNetL2TPTransport, CFStringGetTypeID(), options, service, 0);
if (string) {
if (CFStringCompare(string, kSCValNetL2TPTransportIP, 0) == kCFCompareEqualTo)
addparam(cmdarg, &argi, "l2tpnoipsec");
}
get_str_option(ppp, kSCEntNetL2TP, SCSTR("IPSecSharedSecret"), options, service, sopt, &lval, "");
if (sopt[0]) {
strcat(pipearg, " l2tpipsecsharedsecret \"");
escape_str(pipearg + strlen(pipearg), sizeof(pipearg) - strlen(pipearg) - 1, sopt);
strcat(pipearg, "\"");
}
string = get_cf_option(kSCEntNetL2TP, SCSTR("IPSecSharedSecretEncryption"), CFStringGetTypeID(), options, service, 0);
if (string) {
if (CFStringCompare(string, CFSTR("Key"), 0) == kCFCompareEqualTo)
addstrparam(cmdarg, &argi, "l2tpipsecsharedsecrettype", "key");
else if (CFStringCompare(string, CFSTR("Keychain"), 0) == kCFCompareEqualTo)
addstrparam(cmdarg, &argi, "l2tpipsecsharedsecrettype", "keychain");
}
get_int_option(ppp, SCSTR("L2TP"), SCSTR("UDPPort"), options, service, &lval, 0 );
addintparam(cmdarg, &argi, "l2tpudpport", lval);
break;
case PPP_TYPE_PPTP:
get_int_option(ppp, SCSTR("PPTP"), SCSTR("TCPKeepAlive"), options, service, &lval, 0);
if (lval) {
get_int_option(ppp, SCSTR("PPTP"), SCSTR("TCPKeepAliveTimer"), options, service, &lval, 0);
}
else {
ppp_getoptval(ppp, options, service, PPP_OPT_LCP_ECHO, &lval, &len);
lval = lval >> 16;
}
addintparam(cmdarg, &argi, "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)
addstrparam(cmdarg, &argi, "plugin", "PPPSerial.ppp");
if (lval == PPP_COMM_TERM_WINDOW)
addparam(cmdarg, &argi, "terminalwindow");
else if (lval == PPP_COMM_TERM_SCRIPT)
if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_TERMINALSCRIPT, sopt, &len) && sopt[0])
addstrparam(cmdarg, &argi, "terminalscript", sopt);
}
if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_REMOTEADDR, sopt, &len) && sopt[0])
addstrparam(cmdarg, &argi, "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])
addstrparam(cmdarg, &argi, "altremoteaddress", sopt);
get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPCommRedialCount, options, service, &lval, 0);
if (lval)
addintparam(cmdarg, &argi, "redialcount", lval);
get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPCommRedialInterval, options, service, &lval, 0);
if (lval)
addintparam(cmdarg, &argi, "redialtimer", lval);
}
if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_IDLETIMER, &lval, &len) && lval) {
addintparam(cmdarg, &argi, "idle", lval);
addparam(cmdarg, &argi, "noidlerecv");
}
if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_SESSIONTIMER, &lval, &len) && lval)
addintparam(cmdarg, &argi, "maxconnect", lval);
if (dialondemand) {
addparam(cmdarg, &argi, "demand");
addintparam(cmdarg, &argi, "holdoff", 30);
get_int_option(ppp, kSCEntNetPPP, SCSTR("MaxFailure"), 0, service, &lval, 3);
addintparam(cmdarg, &argi, "maxfail", lval);
}
if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_ECHO, &lval, &len) && lval) {
if (lval >> 16)
addintparam(cmdarg, &argi, "lcp-echo-interval", lval >> 16);
if (lval & 0xffff)
addintparam(cmdarg, &argi, "lcp-echo-failure", lval & 0xffff);
}
if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_HDRCOMP, &lval, &len)) {
if (!(lval & 1))
addparam(cmdarg, &argi, "nopcomp");
if (!(lval & 2))
addparam(cmdarg, &argi, "noaccomp");
}
if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_MRU, &lval, &len) && lval)
addintparam(cmdarg, &argi, "mru", lval);
if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_MTU, &lval, &len) && lval)
addintparam(cmdarg, &argi, "mtu", lval);
if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_RCACCM, &lval, &len) && lval)
addintparam(cmdarg, &argi, "asyncmap", lval);
else
addparam(cmdarg, &argi, "receive-all");
if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_TXACCM, &lval, &len) && lval) {
addparam(cmdarg, &argi, "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; addparam(cmdarg, &argi, str);
}
if (!existEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetIPv4)) {
addparam(cmdarg, &argi, "noip");
}
else {
#if 0
if (getString(service, CFSTR("IPCPUpScript"), str, sizeof(str)) && str[0])
addstrparam(cmdarg, &argi, "ip-up", str);
if (getString(service, CFSTR("IPCPDownScript"), str, sizeof(str)) && str[0])
addstrparam(cmdarg, &argi, "ip-down", str);
#endif
if (getStringFromEntity(kSCDynamicStoreDomainState, 0,
kSCEntNetIPv4, kSCPropNetIPv4Router, sopt, OPT_STR_LEN) && sopt[0])
addstrparam(cmdarg, &argi, "ipparam", sopt);
get_int_option(ppp, kSCEntNetIPv4, kSCPropNetOverridePrimary, 0 , service, &lval, 0);
if (lval)
addparam(cmdarg, &argi, "defaultroute");
if (! (ppp_getoptval(ppp, options, service, PPP_OPT_IPCP_HDRCOMP, &lval, &len) && lval))
addparam(cmdarg, &argi, "novj");
if (ppp->subtype == PPP_TYPE_L2TP || ppp->subtype == PPP_TYPE_PPTP ) {
addintparam(cmdarg, &argi, "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);
addparam(cmdarg, &argi, str);
addparam(cmdarg, &argi, "noipdefault");
addparam(cmdarg, &argi, "ipcp-accept-local");
addparam(cmdarg, &argi, "ipcp-accept-remote");
get_addr_option(ppp, kSCEntNetDNS, kSCPropNetDNSServerAddresses, options, service, &lval, -1);
if (lval == -1)
addparam(cmdarg, &argi, "usepeerdns");
}
if (!existEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetIPv6)) {
}
else {
addparam(cmdarg, &argi, "+ipv6");
addparam(cmdarg, &argi, "ipv6cp-use-persistent");
}
get_int_option(ppp, kSCEntNetPPP, SCSTR("ACSPEnabled"), options, service, &lval, 0);
if (lval == 0)
addparam(cmdarg, &argi, "noacsp");
addparam(cmdarg, &argi, "noauth");
if (ppp_getoptval(ppp, options, service, PPP_OPT_AUTH_PROTO, &lval, &len) && (lval != PPP_AUTH_NONE)) {
if (ppp_getoptval(ppp, options, service, PPP_OPT_AUTH_NAME, sopt, &len) && sopt[0]) {
addstrparam(cmdarg, &argi, "user", sopt);
lval1 = get_str_option(ppp, kSCEntNetPPP, kSCPropNetPPPAuthPassword, options, service, sopt, &lval, "");
if (sopt[0]) {
if ((lval1 == 3)
&& (string = CFDictionaryGetValue(pppdict, kSCPropNetPPPAuthPasswordEncryption))
&& (CFGetTypeID(string) == CFStringGetTypeID())
&& (CFStringCompare(string, SCSTR("Keychain"), 0) == kCFCompareEqualTo)) {
addstrparam(cmdarg, &argi, "keychainpassword", sopt);
}
else {
strcat(pipearg, " password \"");
escape_str(pipearg + strlen(pipearg), sizeof(pipearg) - strlen(pipearg) - 1, sopt);
strcat(pipearg, "\"");
}
}
else {
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 (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"); addstrparam(cmdarg, &argi, "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)) {
addparam(cmdarg, &argi, "mppe-stateless");
addparam(cmdarg, &argi, "mppe-128");
addparam(cmdarg, &argi, "mppe-40");
if (auth_default)
auth_bits = 0xc;
}
else {
addparam(cmdarg, &argi, "noccp");
}
if ((auth_bits & 1) == 0)
addparam(cmdarg, &argi, "refuse-pap");
if ((auth_bits & 2) == 0)
addparam(cmdarg, &argi, "refuse-chap-md5");
if ((auth_bits & 4) == 0)
addparam(cmdarg, &argi, "refuse-mschap");
if ((auth_bits & 8) == 0)
addparam(cmdarg, &argi, "refuse-mschap-v2");
if ((auth_bits & 0x10) == 0)
addparam(cmdarg, &argi, "refuse-eap");
if (auth_bits == 0x10)
needpasswd = 0;
if (!(ppp->alertenable & PPP_ALERT_PASSWORDS) || !needpasswd)
addparam(cmdarg, &argi, "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))
addparam(cmdarg, &argi, "askpasswordafter");
}
addparam(cmdarg, &argi, "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)
addintparam(cmdarg, &argi, "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"); addstrparam(cmdarg, &argi, "plugin", str);
}
}
}
get_str_option(ppp, kSCEntNetPPP, kSCPropUserDefinedName, options, service, sopt, &lval, "");
if (sopt[0])
addstrparam(cmdarg, &argi, "call", sopt);
CFRelease(service);
#if 0
printf("/usr/sbin/pppd ");
for (i = 1; i < MAXARG; i++) {
if (cmdarg[i]) {
printf("%d : %s\n", i, cmdarg[i]);
}
}
printf("\n");
#endif
ppp->lastdevstatus = 0;
if ((pipe(ppp->iFD) == -1)
|| ((ppp->pid = _SCDPluginExecCommand2(exec_callback, (void*)ppp_makeref(ppp), 0, 0, PATH_PPPD, cmdarg, exec_postfork, (void*)ppp_makeref(ppp))) == -1)) {
ppp->laststatus = EXIT_FATAL_ERROR;
ppp_display_error(ppp);
}
else {
write(ppp->iFD[WRITE], pipearg, strlen(pipearg));
close(ppp->iFD[WRITE]);
if (client)
ppp_addclient(ppp, client, autoclose);
ppp->laststatus = EXIT_OK;
ppp->started_link = 1;
ppp->oldphase = PPP_INITIALIZE;
ppp_setphase(ppp, PPP_INITIALIZE);
ppp->dialondemand = dialondemand;
ppp->connectopts = options;
if (options)
CFRetain(options);
}
for (i = 0; i < MAXARG; i++) {
if (cmdarg[i]) {
free(cmdarg[i]);
cmdarg[i] = 0;
}
}
return ppp->laststatus;
}
static void
exec_postfork(pid_t pid, void *arg)
{
struct ppp *ppp = ppp_findbyref((u_int32_t)arg);
if (ppp == 0) return;
if (pid) {
int yes = 1;
close(ppp->iFD[READ]);
if (ioctl(ppp->iFD[WRITE], FIONBIO, &yes) == -1) {
}
} else {
int i;
close(ppp->iFD[WRITE]);
if (ppp->iFD[READ] != STDIN_FILENO) {
dup2(ppp->iFD[READ], STDIN_FILENO);
}
close(STDOUT_FILENO);
open(_PATH_DEVNULL, O_RDWR, 0);
close(STDERR_FILENO);
open(_PATH_DEVNULL, O_RDWR, 0);
for (i = getdtablesize() - 1; i > STDERR_FILENO; i--) close(i);
}
return;
}
void exec_callback(pid_t pid, int status, struct rusage *rusage, void *context)
{
struct ppp *ppp;
u_int32_t error;
if (status == 0) return;
ppp = ppp_findbyref((u_int32_t)context);
if (ppp == 0) return;
if (ppp->pid != pid) return;
if (ppp->started_link == 0) return;
ppp->laststatus = (status >> 8) & 0xFF;
ppp_setphase(ppp, PPP_IDLE);
ppp->started_link = 0;
ppp->dialondemand = 0;
ppp_display_error(ppp);
if (ppp->connectopts) {
CFRelease(ppp->connectopts);
ppp->connectopts = 0;
}
error = ppp_translate_error(ppp->subtype, ppp->laststatus, 0);
client_notify(ppp->sid, ppp_makeref(ppp), PPP_EVT_DISCONNECTED, error, 1);
}
int ppp_clientgone(void *client)
{
struct ppp *ppp;
struct ppp_client *pppclient;
TAILQ_FOREACH(ppp, &ppp_head, next) {
pppclient = ppp_getclient(ppp, client);
if (pppclient && pppclient->autoclose) {
ppp_dodisconnect(ppp, 15, client);
}
}
return 0;
}
int ppp_dodisconnect(struct ppp *ppp, int sig, void *client)
{
int pid, dokill = 1;
if (client) {
if (ppp_getclient(ppp, client))
ppp_delclient(ppp, client);
if (TAILQ_FIRST(&ppp->client_head))
return 0;
}
else {
ppp_delclients(ppp);
}
if (ppp->phase != PPP_IDLE && !ppp->kill_link && !ppp->kill_sent) {
switch (ppp->phase) {
case PPP_INITIALIZE:
if (ppp->needconnect) {
ppp->needconnect = 0;
if (ppp->needconnectopts)
CFRelease(ppp->needconnectopts);
ppp->needconnectopts = 0;
}
dokill = 0; case PPP_CONNECTLINK:
ppp_setphase(ppp, PPP_DISCONNECTLINK);
break;
case PPP_DISCONNECTLINK:
case PPP_TERMINATE:
return 0;
default:
ppp_setphase(ppp, PPP_TERMINATE);
}
if (dokill
&& getNumberFromEntity(kSCDynamicStoreDomainState, ppp->serviceID, kSCEntNetPPP,
CFSTR("pid") , &pid)) {
ppp->started_link = 0;
ppp->kill_sent = 1;
kill(pid, sig);
}
else {
ppp->kill_link = sig; }
}
return 0;
}
int ppp_dosuspend(struct ppp *ppp)
{
int pid;
if (ppp->phase != PPP_IDLE
&& getNumberFromEntity(kSCDynamicStoreDomainState, ppp->serviceID, kSCEntNetPPP,
CFSTR("pid") , &pid)) {
kill(pid, SIGTSTP);
}
return 0;
}
int ppp_doresume(struct ppp *ppp)
{
int pid;
if (ppp->phase != PPP_IDLE
&& getNumberFromEntity(kSCDynamicStoreDomainState, ppp->serviceID, kSCEntNetPPP,
CFSTR("pid") , &pid)) {
kill(pid, SIGCONT);
}
return 0;
}
int ppp_addclient(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;
}
int ppp_delclient(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;
}
struct ppp_client *ppp_getclient(struct ppp *ppp, void *client)
{
struct ppp_client *pppclient;
TAILQ_FOREACH(pppclient, &ppp->client_head, next)
if (pppclient->client == client)
return pppclient;
return 0;
}
int ppp_delclients(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;
}