#include <string.h>
#include <stdio.h>
#include <sys/errno.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#include <net/dlil.h>
#include <sys/param.h>
#include <sys/fcntl.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#define SCLog
#include "ppp_msg.h"
#include "ppp_privmsg.h"
#include "ppp_client.h"
#include "ppp_manager.h"
#include "ppp_option.h"
#include "ppp_command.h"
enum {
do_nothing = 0,
do_process,
do_close,
do_error
};
#define ICON "NetworkConnect.icns"
int initThings();
int startListen();
u_long processRequest (struct client *client, struct msg *msg);
u_long close_cleanup (struct client *client, struct msg *msg);
void listenCallBack(CFSocketRef s, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info);
void clientCallBack(CFSocketRef s, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info);
CFStringRef gPluginsDir = 0;
CFBundleRef gBundleRef = 0;
CFURLRef gBundleURLRef = 0;
CFStringRef gCancelRef = 0;
CFStringRef gInternetConnectRef = 0;
CFURLRef gIconURLRef = 0;
void load(CFBundleRef bundle, Boolean debug)
{
gBundleRef = bundle;
if (initThings())
return;
if (startListen())
return;
if (client_init_all())
return;
CFRetain(bundle);
}
void prime()
{
ppp_init_all();
}
int initThings()
{
CFURLRef urlref, absurlref;
gBundleURLRef = CFBundleCopyBundleURL(gBundleRef);
urlref = CFBundleCopyBuiltInPlugInsURL(gBundleRef);
if (urlref) {
absurlref = CFURLCopyAbsoluteURL(urlref);
if (absurlref) {
gPluginsDir = CFURLCopyPath(absurlref);
CFRelease(absurlref);
}
CFRelease(urlref);
}
gCancelRef = CFBundleCopyLocalizedString(gBundleRef, CFSTR("Cancel"), CFSTR("Cancel"), NULL);
gInternetConnectRef = CFBundleCopyLocalizedString(gBundleRef, CFSTR("Internet Connect"), CFSTR("Internet Connect"), NULL);
gIconURLRef = CFBundleCopyResourceURL(gBundleRef, CFSTR(ICON), NULL, NULL);
return 0;
}
int startListen ()
{
struct sockaddr_un addr;
int error, s;
mode_t mask;
CFSocketRef ref = 0;
CFRunLoopSourceRef rls;
CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1)
goto fail;
unlink(PPP_PATH);
bzero(&addr, sizeof(addr));
addr.sun_family = AF_LOCAL;
strcpy(addr.sun_path, PPP_PATH);
mask = umask(0);
error = bind(s, (struct sockaddr *)&addr, SUN_LEN(&addr));
umask(mask);
if (error)
goto fail;
if ((ref = CFSocketCreateWithNative(NULL, s, kCFSocketReadCallBack,
listenCallBack, &context)) == 0)
goto fail;
if ((rls = CFSocketCreateRunLoopSource(NULL, ref, 0)) == 0)
goto fail;
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
listen(s, SOMAXCONN);
CFRelease(ref);
return 0;
fail:
SCLog(TRUE, LOG_INFO, CFSTR("PPPController: initialization failed...\n"));
if (s != -1)
close(s);
if (ref) {
CFSocketInvalidate(ref);
CFRelease(ref);
}
return 1;
}
void listenCallBack(CFSocketRef inref, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info)
{
struct sockaddr_un addr;
int s, len, flags;
CFSocketRef ref;
CFRunLoopSourceRef rls;
CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
len = sizeof(addr);
if ((s = accept(CFSocketGetNative(inref), (struct sockaddr *) &addr, &len)) == -1)
return;
PRINTF(("Accepted connection...\n"));
if ((flags = fcntl(s, F_GETFL)) == -1
|| fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) {
SCLog(TRUE, LOG_INFO, CFSTR("Couldn't set accepting socket in non-blocking mode, errno = %d\n"), errno);
}
if ((ref = CFSocketCreateWithNative(NULL, s,
kCFSocketReadCallBack, clientCallBack, &context)) == 0) {
close(s);
return;
}
if ((rls = CFSocketCreateRunLoopSource(NULL, ref, 0)) == 0)
goto fail;
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
if (client_new(ref) == 0)
goto fail;
CFRelease(ref);
return;
fail:
CFSocketInvalidate(ref);
CFRelease(ref);
return;
}
int
readn(int ref, void *data, int len)
{
int n, left = len;
void *p = data;
while (left > 0) {
if ((n = read(ref, p, left)) < 0) {
if (errno == EWOULDBLOCK)
return (len - left);
if (errno != EINTR)
return -1;
n = 0;
}
else if (n == 0)
return -1;
left -= n;
p += n;
}
return (len - left);
}
int
writen(int ref, void *data, int len)
{
int n, left = len;
void *p = data;
while (left > 0) {
if ((n = write(ref, p, left)) <= 0) {
if (errno != EINTR)
return -1;
n = 0;
}
left -= n;
p += n;
}
return len;
}
void clientCallBack(CFSocketRef inref, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info)
{
int s = CFSocketGetNative(inref);
int action = do_nothing;
ssize_t n;
struct client *client;
client = client_findbysocketref(inref);
if (client == 0)
return;
if (client->msglen < sizeof(struct ppp_msg_hdr)) {
n = readn(s, &((u_int8_t *)&client->msghdr)[client->msglen], sizeof(struct ppp_msg_hdr) - client->msglen);
switch (n) {
case -1:
action = do_close;
break;
default:
client->msglen += n;
if (client->msglen == sizeof(struct ppp_msg_hdr)) {
client->msgtotallen = client->msglen
+ client->msghdr.m_len
+ (client->msghdr.m_flags & USE_SERVICEID ? client->msghdr.m_link : 0);
client->msg = CFAllocatorAllocate(NULL, client->msgtotallen + 1, 0);
if (client->msg == 0)
action = do_error;
else {
bcopy(&client->msghdr, client->msg, sizeof(struct ppp_msg_hdr));
client->msg[client->msgtotallen] = 0;
}
}
}
}
if (client->msglen >= sizeof(struct ppp_msg_hdr)) {
n = readn(s, &client->msg[client->msglen], client->msgtotallen - client->msglen);
switch (n) {
case -1:
action = do_close;
break;
default:
client->msglen += n;
if (client->msglen == client->msgtotallen) {
action = do_process;
}
}
}
switch (action) {
case do_nothing:
break;
case do_error:
case do_close:
PRINTF(("Connection closed...\n"));
CFSocketInvalidate(inref);
client_dispose(client);
break;
case do_process:
processRequest(client, (struct msg *)client->msg);
CFAllocatorDeallocate(NULL, client->msg);
client->msg = 0;
client->msglen = 0;
client->msgtotallen = 0;
break;
}
}
typedef u_long (*msg_function)(struct client *client, struct msg *msg, void **reply);
msg_function requests[] = {
NULL,
ppp_version,
ppp_status,
ppp_connect,
NULL,
ppp_disconnect,
ppp_getoption,
ppp_setoption,
ppp_enable_event,
ppp_disable_event,
NULL,
ppp_getnblinks,
ppp_getlinkbyindex,
ppp_getlinkbyserviceid,
ppp_getlinkbyifname,
ppp_suspend,
ppp_resume,
ppp_extendedstatus,
ppp_getconnectdata
};
#define LAST_REQUEST PPP_GETCONNECTDATA
u_long processRequest (struct client *client, struct msg *msg)
{
void *reply = 0;
msg_function func;
PRINTF(("process_request : type = %x, len = %d\n", msg->hdr.m_type, msg->hdr.m_len));
if (msg->hdr.m_type <= LAST_REQUEST) {
func = requests[msg->hdr.m_type];
if (func)
(*func)(client, msg, &reply);
}
else {
switch (msg->hdr.m_type) {
case PPPD_EVENT:
ppp_event(client, msg);
break;
}
}
if (msg->hdr.m_len != 0xFFFFFFFF) {
writen(CFSocketGetNative(client->ref), msg, sizeof(struct ppp_msg_hdr) +
(msg->hdr.m_flags & USE_SERVICEID ? msg->hdr.m_link : 0));
if (msg->hdr.m_len) {
writen(CFSocketGetNative(client->ref), reply, msg->hdr.m_len);
CFAllocatorDeallocate(NULL, reply);
}
PRINTF(("process_request : m_type = 0x%x, result = 0x%x, cookie = 0x%x, link = 0x%x, len = 0x%x\n",
msg->hdr.m_type, msg->hdr.m_result, msg->hdr.m_cookie, msg->hdr.m_link, msg->hdr.m_len));
#if 0
if (msg->hdr.m_type == PPP_STATUS) {
struct ppp_status *stat = (struct ppp_status *)&msg->data[0];
PRINTF((" ----- status = 0x%x", stat->status));
if (stat->status != PPP_RUNNING) {
PRINTF((", cause = 0x%x", stat->s.disc.lastDiscCause));
}
PRINTF(("\n"));
}
#endif
}
return 0;
}