#include <string.h>
#include <stdio.h>
#include <termios.h>
#include <sys/errno.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <net/if.h>
#include <CoreFoundation/CoreFoundation.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <SystemConfiguration/SystemConfiguration.h>
#define SCLog
#include "ppp_msg.h"
#include "../Family/if_ppplink.h"
#include "ppp_client.h"
#include "ppp_option.h"
#include "ppp_manager.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)
static __inline__ CFTypeRef isA_CFType(CFTypeRef obj, CFTypeID type);
static __inline__ CFTypeRef isA_CFDictionary(CFTypeRef obj);
static __inline__ void my_CFRelease(CFTypeRef obj);
static CFStringRef parse_component(CFStringRef key, CFStringRef prefix);
static u_int32_t CFStringAddrToLong(CFStringRef string);
static void reorder_services();
static int process_servicestate(CFStringRef serviceID);
static int process_servicesetup(CFStringRef serviceID);
static void cache_notifier(SCDynamicStoreRef session, CFArrayRef changedKeys, void *info);
CFStringRef gLoggedInUser = NULL;
SCDynamicStoreRef gCfgCache = NULL;
int options_init_all()
{
CFStringRef key = NULL, setup = NULL;
CFMutableArrayRef keys = NULL, patterns = NULL;
CFArrayRef services = NULL;
int i, ret = 0;
CFRunLoopSourceRef rls = NULL;
CFStringRef notifs[] = {
kSCEntNetPPP,
kSCEntNetModem,
kSCEntNetInterface,
kSCEntNetIPv4,
kSCEntNetDNS,
NULL,
};
if ((gCfgCache = SCDynamicStoreCreate(0, CFSTR("PPPController"), cache_notifier, 0)) == NULL)
goto fail;
if ((rls = SCDynamicStoreCreateRunLoopSource(0, gCfgCache, 0)) == NULL)
goto fail;
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
gLoggedInUser = SCDynamicStoreCopyConsoleUser(gCfgCache, 0, 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 = CREATESERVICESTATE(kSCEntNetPPP)) == NULL)
goto fail;
CFArrayAppendValue(patterns, key);
CFRelease(key);
if ((key = CREATEGLOBALSETUP(kSCEntNetIPv4)) == NULL)
goto fail;
CFArrayAppendValue(keys, key);
CFRelease(key);
if ((key = SCDynamicStoreKeyCreateConsoleUser(0)) == NULL)
goto fail;
CFArrayAppendValue(keys, key);
CFRelease(key);
SCDynamicStoreSetNotificationKeys(gCfgCache, keys, patterns);
key = CREATESERVICESETUP(kSCEntNetPPP);
setup = CREATEPREFIXSETUP();
if (key == NULL || setup == NULL)
goto fail;
services = SCDynamicStoreCopyKeyList(gCfgCache, key);
if (services == NULL)
goto done;
for (i = 0; i < CFArrayGetCount(services); i++) {
CFStringRef serviceID;
if (serviceID = parse_component(CFArrayGetValueAtIndex(services, i), setup)) {
process_servicesetup(serviceID);
CFRelease(serviceID);
}
}
reorder_services();
ppp_postupdatesetup();
done:
my_CFRelease(services);
my_CFRelease(key);
my_CFRelease(setup);
my_CFRelease(keys);
my_CFRelease(patterns);
return ret;
fail:
SCLog(TRUE, LOG_ERR, CFSTR("PPPController options_init_all : allocation failed, error = %s\n"),
SCErrorString(SCError()));
my_CFRelease(gCfgCache);
ret = 1;
goto done;
}
int process_servicesetup(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) {
ppp_dispose(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)) {
ppp_dispose(ppp);
ppp = 0;
}
if (!ppp) {
ppp = ppp_new(serviceID, subtype);
if (!ppp)
goto done;
}
ppp_updatesetup(ppp, service);
done:
my_CFRelease(interface);
my_CFRelease(service);
return 0;
}
int process_servicestate(CFStringRef serviceID)
{
CFDictionaryRef service = NULL;
struct ppp *ppp;
ppp = ppp_findbyserviceID(serviceID);
if (!ppp)
return 0;
service = copyEntity(kSCDynamicStoreDomainState, serviceID, kSCEntNetPPP);
if (!service)
return 0;
ppp_updatestate(ppp, service);
CFRelease(service);
return 0;
}
void reorder_services()
{
CFDictionaryRef ip_dict = NULL;
CFStringRef key, serviceID;
CFArrayRef serviceorder;
int i;
struct ppp *ppp;
key = CREATEGLOBALSETUP(kSCEntNetIPv4);
if (key) {
ip_dict = (CFDictionaryRef)SCDynamicStoreCopyValue(gCfgCache, key);
if (ip_dict) {
serviceorder = CFDictionaryGetValue(ip_dict, kSCPropNetServiceOrder);
if (serviceorder) {
for (i = 0; i < CFArrayGetCount(serviceorder); i++) {
serviceID = CFArrayGetValueAtIndex(serviceorder, i);
if (ppp = ppp_findbyserviceID(serviceID))
ppp_setorder(ppp, 0xffff);
}
}
CFRelease(ip_dict);
}
CFRelease(key);
}
}
void cache_notifier(SCDynamicStoreRef session, CFArrayRef changedKeys, void *info)
{
CFStringRef setup, state, userkey, ipkey;
u_long i, doreorder = 0, dopostsetup = 0;
if (changedKeys == NULL)
return;
setup = CREATEPREFIXSETUP();
state = CREATEPREFIXSTATE();
userkey = SCDynamicStoreKeyCreateConsoleUser(0);
ipkey = CREATEGLOBALSETUP(kSCEntNetIPv4);
if (setup == NULL || state == NULL || userkey == NULL || ipkey == NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("PPPController cache_notifier : can't allocate keys\n"));
goto done;
}
for (i = 0; i < CFArrayGetCount(changedKeys); i++) {
CFStringRef change, serviceID;
change = CFArrayGetValueAtIndex(changedKeys, i);
if (CFEqual(change, userkey)) {
my_CFRelease(gLoggedInUser);
if (gLoggedInUser = SCDynamicStoreCopyConsoleUser(session, 0, 0))
ppp_login(); else
ppp_logout(); continue;
}
if (CFEqual(change, ipkey)) {
doreorder = 1;
continue;
}
serviceID = parse_component(change, setup);
if (serviceID) {
process_servicesetup(serviceID);
CFRelease(serviceID);
dopostsetup = 1;
continue;
}
serviceID = parse_component(change, state);
if (serviceID) {
process_servicestate(serviceID);
CFRelease(serviceID);
continue;
}
}
if (doreorder) {
reorder_services();
}
if (dopostsetup)
ppp_postupdatesetup();
done:
my_CFRelease(setup);
my_CFRelease(state);
my_CFRelease(userkey);
my_CFRelease(ipkey);
return;
}
static __inline__ CFTypeRef isA_CFType(CFTypeRef obj, CFTypeID type)
{
if (obj == NULL)
return (NULL);
if (CFGetTypeID(obj) != type)
return (NULL);
return (obj);
}
static __inline__ CFTypeRef isA_CFDictionary(CFTypeRef obj)
{
return (isA_CFType(obj, CFDictionaryGetTypeID()));
}
static __inline__ void my_CFRelease(CFTypeRef obj)
{
if (obj)
CFRelease(obj);
return;
}
static CFStringRef parse_component(CFStringRef key, CFStringRef prefix)
{
CFMutableStringRef comp;
CFRange range;
if (!CFStringHasPrefix(key, prefix))
return NULL;
comp = CFStringCreateMutableCopy(NULL, 0, key);
CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
range = CFStringFind(comp, CFSTR("/"), 0);
if (range.location == kCFNotFound) {
CFRelease(comp);
return NULL;
}
range.length = CFStringGetLength(comp) - range.location;
CFStringDelete(comp, range);
return comp;
}
CFTypeRef copyEntity(CFStringRef domain, CFStringRef serviceID, CFStringRef entity)
{
CFTypeRef data = NULL;
CFStringRef key;
if (serviceID)
key = SCDynamicStoreKeyCreateNetworkServiceEntity(0, domain, serviceID, entity);
else
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(0, domain, entity);
if (key) {
data = SCDynamicStoreCopyValue(gCfgCache, key);
CFRelease(key);
}
return data;
}
int getString(CFDictionaryRef service, CFStringRef property, u_char *str, u_int16_t maxlen)
{
CFStringRef string;
CFDataRef ref;
str[0] = 0;
ref = CFDictionaryGetValue(service, property);
if (ref) {
if (CFGetTypeID(ref) == CFStringGetTypeID()) {
CFStringGetCString((CFStringRef)ref, str, maxlen, kCFStringEncodingUTF8);
return 0;
}
else if (CFGetTypeID(ref) == CFDataGetTypeID()) {
string = CFStringCreateWithCharacters(NULL,
(UniChar *)CFDataGetBytePtr(ref), CFDataGetLength(ref)/sizeof(UniChar));
if (string) {
CFStringGetCString(string, str, maxlen, kCFStringEncodingUTF8);
CFRelease(string);
return 0;
}
}
}
return -1;
}
int getNumber(CFDictionaryRef dict, CFStringRef property, u_int32_t *outval)
{
CFNumberRef ref;
ref = CFDictionaryGetValue(dict, property);
if (ref && (CFGetTypeID(ref) == CFNumberGetTypeID())) {
CFNumberGetValue(ref, kCFNumberSInt32Type, outval);
return 0;
}
return -1;
}
int getNumberFromEntity(CFStringRef domain, CFStringRef serviceID,
CFStringRef entity, CFStringRef property, u_int32_t *outval)
{
CFTypeRef data;
int err = -1;
if (data = copyEntity(domain, serviceID, entity)) {
err = getNumber(data, property, outval);
CFRelease(data);
}
return err;
}
int getStringFromEntity(CFStringRef domain, CFStringRef serviceID,
CFStringRef entity, CFStringRef property, u_char *str, u_int16_t maxlen)
{
CFTypeRef data;
int err = -1;
data = copyEntity(domain, serviceID, entity);
if (data) {
err = getString(data, property, str, maxlen);
CFRelease(data);
}
return err;
}
u_int32_t CFStringAddrToLong(CFStringRef string)
{
u_char str[100];
u_int32_t ret = 0;
if (string) {
str[0] = 0;
CFStringGetCString(string, str, sizeof(str), kCFStringEncodingMacRoman);
ret = inet_addr(str);
}
return ret;
}
int getAddressFromEntity(CFStringRef domain, CFStringRef serviceID,
CFStringRef entity, CFStringRef property, u_int32_t *outval)
{
CFTypeRef data;
int err = -1;
CFArrayRef array;
data = copyEntity(domain, serviceID, entity);
if (data) {
array = CFDictionaryGetValue(data, property);
if (array && CFArrayGetCount(array)) {
*outval = CFStringAddrToLong(CFArrayGetValueAtIndex(array, 0));
err = 0;
}
CFRelease(data);
}
return err;
}
int getServiceName(CFStringRef serviceID, u_char *str, u_int16_t maxlen)
{
CFTypeRef data;
int err = -1;
data = copyEntity(kSCDynamicStoreDomainSetup, serviceID, 0);
if (data) {
err = getString(data, kSCPropUserDefinedName, str, maxlen);
CFRelease(data);
}
return err;
}