ppp_socket_server.c [plain text]
#include <string.h>
#include <stdio.h>
#include <sys/errno.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/ucred.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h> // for SCLog()
#include "ppp_msg.h"
#include "scnc_main.h"
#include "ppp_privmsg.h"
#include "scnc_client.h"
#include "ppp_manager.h"
#include "ppp_option.h"
#include "ppp_socket_server.h"
#include "scnc_utils.h"
enum {
do_nothing = 0,
do_process,
do_close,
do_error
};
static void socket_status (struct client *client, struct msg *msg, void **reply);
static void socket_extendedstatus (struct client *client, struct msg *msg, void **reply);
static void socket_connect (struct client *client, struct msg *msg, void **reply);
static void socket_disconnect (struct client *client, struct msg *msg, void **reply);
static void socket_suspend (struct client *client, struct msg *msg, void **reply);
static void socket_resume (struct client *client, struct msg *msg, void **reply);
static void socket_getconnectdata (struct client *client, struct msg *msg, void **reply);
static void socket_enable_event (struct client *client, struct msg *msg, void **reply);
static void socket_disable_event (struct client *client, struct msg *msg, void **reply);
static void socket_version (struct client *client, struct msg *msg, void **reply);
static void socket_getnblinks (struct client *client, struct msg *msg, void **reply);
static void socket_getlinkbyindex (struct client *client, struct msg *msg, void **reply);
static void socket_getlinkbyserviceid (struct client *client, struct msg *msg, void **reply);
static void socket_getlinkbyifname (struct client *client, struct msg *msg, void **reply);
static void socket_setoption (struct client *client, struct msg *msg, void **reply);
static void socket_getoption (struct client *client, struct msg *msg, void **reply);
static void socket_pppd_event(struct client *client, struct msg *msg);
static void socket_pppd_status(struct client *client, struct msg *msg);
static void socket_pppd_phase(struct client *client, struct msg *msg);
static void processRequest (struct client *client, struct msg *msg);
static void listenCallBack(CFSocketRef s, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info);
static void clientCallBack(CFSocketRef s, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info);
extern TAILQ_HEAD(, service) service_head;
int ppp_socket_start_server ()
{
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;
}
int ppp_socket_create_client(int s, int priviledged, uid_t uid, gid_t gid)
{
int flags;
CFSocketRef ref;
CFRunLoopSourceRef rls;
CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
if ((flags = fcntl(s, F_GETFL)) == -1
|| fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) {
SCLog(TRUE, LOG_INFO, CFSTR("Couldn't set client socket in non-blocking mode, errno = %d\n"), errno);
}
if ((ref = CFSocketCreateWithNative(NULL, s,
kCFSocketReadCallBack, clientCallBack, &context)) == 0) {
close(s);
return -1;
}
if ((rls = CFSocketCreateRunLoopSource(NULL, ref, 0)) == 0)
goto fail;
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
if (client_new_socket(ref, priviledged, uid, gid) == 0)
goto fail;
CFRelease(ref);
return 0;
fail:
CFSocketInvalidate(ref);
CFRelease(ref);
return -1;
}
static
void listenCallBack(CFSocketRef inref, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info)
{
struct sockaddr_un addr;
int s, len;
struct xucred xucred;
len = sizeof(addr);
if ((s = accept(CFSocketGetNative(inref), (struct sockaddr *) &addr, &len)) == -1)
return;
PRINTF(("Accepted connection...\n"));
len = sizeof(xucred);
if (getsockopt(s, 0, LOCAL_PEERCRED, &xucred, &len) == -1) {
SCLog(TRUE, LOG_ERR, CFSTR("PPPController: can't get LOCAL_PEERCRED, errno = %d\n"), errno);
return ;
}
ppp_socket_create_client(s, 0, 0 , 0 );
}
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;
}
typedef void (*msg_function)(struct client *client, struct msg *msg, void **reply);
msg_function requests[] = {
NULL,
socket_version,
socket_status,
socket_connect,
NULL,
socket_disconnect,
socket_getoption,
socket_setoption,
socket_enable_event,
socket_disable_event,
NULL,
socket_getnblinks,
socket_getlinkbyindex,
socket_getlinkbyserviceid,
socket_getlinkbyifname,
socket_suspend,
socket_resume,
socket_extendedstatus,
socket_getconnectdata
};
#define LAST_REQUEST PPP_GETCONNECTDATA
static
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;
goto clientCallBackPerformAction;
default:
client->msglen += n;
if (client->msglen == sizeof(struct ppp_msg_hdr)) {
if (!(client->flags & CLIENT_FLAG_PRIVILEDGED) && (client->msghdr.m_type > LAST_REQUEST)) {
client->flags |= CLIENT_FLAG_SWAP_BYTES;
client->msghdr.m_flags = ntohs(client->msghdr.m_flags);
client->msghdr.m_type = ntohs(client->msghdr.m_type);
client->msghdr.m_result = ntohl(client->msghdr.m_result);
client->msghdr.m_cookie = ntohl(client->msghdr.m_cookie);
client->msghdr.m_link = ntohl(client->msghdr.m_link);
client->msghdr.m_len = ntohl(client->msghdr.m_len);
}
else
client->flags &= ~CLIENT_FLAG_SWAP_BYTES;
if (client->msghdr.m_len > PPP_MSG_MAX_DATA_LEN) {
SCLog(TRUE, LOG_ERR, CFSTR("Invalid client message header: length %d...\n"), client->msghdr.m_len);
action = do_error;
goto clientCallBackPerformAction;
}
if (client->msghdr.m_flags & USE_SERVICEID &&
client->msghdr.m_link > PPP_MSG_MAX_SERVICEID_LEN) {
SCLog(TRUE, LOG_ERR, CFSTR("Invalid client message header: service-id %d...\n"), client->msghdr.m_link);
action = do_error;
goto clientCallBackPerformAction;
}
client->msgtotallen = client->msglen
+ client->msghdr.m_len
+ (client->msghdr.m_flags & USE_SERVICEID ? client->msghdr.m_link : 0);
client->msg = my_Allocate(client->msgtotallen + 1);
if (client->msg == 0) {
SCLog(TRUE, LOG_ERR, CFSTR("Failed to allocate client message...\n"));
action = do_error;
goto clientCallBackPerformAction;
} 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:
SCLog(TRUE, LOG_ERR, CFSTR("Failed to read client message...\n"));
action = do_close;
break;
default:
client->msglen += n;
if (client->msglen == client->msgtotallen) {
action = do_process;
}
}
}
clientCallBackPerformAction:
switch (action) {
case do_nothing:
break;
case do_error:
case do_close:
PRINTF(("Connection closed...\n"));
client_dispose(client);
break;
case do_process:
processRequest(client, (struct msg *)client->msg);
my_Deallocate(client->msg, client->msgtotallen + 1);
client->msg = 0;
client->msglen = 0;
client->msgtotallen = 0;
break;
}
}
static
void processRequest (struct client *client, struct msg *msg)
{
void *reply = 0;
msg_function func;
struct ppp_msg_hdr hdr;
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 {
if (client->flags & CLIENT_FLAG_PRIVILEDGED) {
switch (msg->hdr.m_type) {
case PPPD_EVENT:
socket_pppd_event(client, msg);
break;
case PPPD_STATUS:
socket_pppd_status(client, msg);
break;
case PPPD_PHASE:
socket_pppd_phase(client, msg);
break;
}
}
}
bcopy(msg, &hdr, sizeof(hdr));
if (client->flags & CLIENT_FLAG_SWAP_BYTES) {
msg->hdr.m_flags = htons(msg->hdr.m_flags);
msg->hdr.m_type = htons(msg->hdr.m_type);
msg->hdr.m_result = htonl(msg->hdr.m_result);
msg->hdr.m_cookie = htonl(msg->hdr.m_cookie);
msg->hdr.m_link = htonl(msg->hdr.m_link);
msg->hdr.m_len = htonl(msg->hdr.m_len);
}
if (hdr.m_len != 0xFFFFFFFF) {
writen(CFSocketGetNative(client->socketRef), msg, sizeof(struct ppp_msg_hdr) +
(hdr.m_flags & USE_SERVICEID ? hdr.m_link : 0));
if (hdr.m_len) {
writen(CFSocketGetNative(client->socketRef), reply, hdr.m_len);
my_Deallocate(reply, hdr.m_len);
}
PRINTF(("process_request : m_type = 0x%x, result = 0x%x, cookie = 0x%x, link = 0x%x, len = 0x%x\n",
hdr.m_type, hdr.m_result, hdr.m_cookie, hdr.m_link, hdr.m_len));
#if 0
if (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
}
}
struct service *ppp_find(struct msg *msg)
{
if (msg->hdr.m_flags & USE_SERVICEID)
return findbysid(msg->data, msg->hdr.m_link);
else
return findbyref(TYPE_PPP, msg->hdr.m_link);
return 0;
}
static
void socket_status(struct client *client, struct msg *msg, void **reply)
{
struct service *serv = ppp_find(msg);
int err;
u_int16_t replylen;
if (!serv) {
msg->hdr.m_result = ENODEV;
msg->hdr.m_len = 0;
return;
}
err = ppp_getstatus1(serv, reply, &replylen);
if (err) {
msg->hdr.m_result = err;
msg->hdr.m_len = 0;
return;
}
if (client->flags & CLIENT_FLAG_SWAP_BYTES) {
struct ppp_status *stat = (struct ppp_status *)*reply;
stat->status = htonl(stat->status);
stat->s.run.timeElapsed = htonl(stat->s.run.timeElapsed);
stat->s.run.timeRemaining = htonl(stat->s.run.timeRemaining);
stat->s.run.inBytes = htonl(stat->s.run.inBytes);
stat->s.run.inPackets = htonl(stat->s.run.inPackets);
stat->s.run.inErrors = htonl(stat->s.run.inErrors);
stat->s.run.outBytes = htonl(stat->s.run.outBytes);
stat->s.run.outPackets = htonl(stat->s.run.outPackets);
stat->s.run.outErrors = htonl(stat->s.run.outErrors);
}
msg->hdr.m_result = 0;
msg->hdr.m_len = replylen;
}
static
void socket_extendedstatus(struct client *client, struct msg *msg, void **reply)
{
struct service *serv = ppp_find(msg);
u_int16_t replylen = 0;
int err;
if (!serv) {
msg->hdr.m_result = ENODEV;
msg->hdr.m_len = 0;
return;
}
err = ppp_copyextendedstatus(serv, reply, &replylen);
if (err) {
msg->hdr.m_result = err;
msg->hdr.m_len = 0;
return;
}
msg->hdr.m_result = 0;
msg->hdr.m_len = replylen;
}
static
void socket_connect(struct client *client, struct msg *msg, void **reply)
{
void *data = (struct ppp_status *)&msg->data[MSG_DATAOFF(msg)];
struct service *serv = ppp_find(msg);
CFDictionaryRef opts = 0;
if (!serv) {
msg->hdr.m_result = ENODEV;
msg->hdr.m_len = 0;
return;
}
if (msg->hdr.m_len == 0) {
opts = client_findoptset(client, serv->serviceID);
}
else {
opts = (CFDictionaryRef)Unserialize(data, msg->hdr.m_len);
if (opts == 0 || CFGetTypeID(opts) != CFDictionaryGetTypeID()) {
msg->hdr.m_result = ENOMEM;
msg->hdr.m_len = 0;
if (opts)
CFRelease(opts);
return;
}
}
msg->hdr.m_result = scnc_start(serv, opts,
(msg->hdr.m_flags & CONNECT_ARBITRATED_FLAG) ? client : 0,
(msg->hdr.m_flags & CONNECT_AUTOCLOSE_FLAG) ? 1 : 0, client->uid, client->gid, 0);
if (opts && msg->hdr.m_len)
CFRelease(opts);
msg->hdr.m_len = 0;
}
static
void socket_disconnect(struct client *client, struct msg *msg, void **reply)
{
struct service *serv = ppp_find(msg);
if (!serv) {
msg->hdr.m_result = ENODEV;
msg->hdr.m_len = 0;
return;
}
scnc_stop(serv, (msg->hdr.m_flags & DISCONNECT_ARBITRATED_FLAG) ? client : 0, SIGHUP);
msg->hdr.m_result = 0;
msg->hdr.m_len = 0;
}
static
void socket_suspend(struct client *client, struct msg *msg, void **reply)
{
struct service *serv = ppp_find(msg);
if (!serv) {
msg->hdr.m_result = ENODEV;
msg->hdr.m_len = 0;
return;
}
ppp_suspend(serv);
msg->hdr.m_result = 0;
msg->hdr.m_len = 0;
}
static
void socket_resume(struct client *client, struct msg *msg, void **reply)
{
struct service *serv = ppp_find(msg);
if (!serv) {
msg->hdr.m_result = ENODEV;
msg->hdr.m_len = 0;
return;
}
ppp_resume(serv);
msg->hdr.m_result = 0;
msg->hdr.m_len = 0;
}
static
void socket_getconnectdata(struct client *client, struct msg *msg, void **reply)
{
struct service *serv = ppp_find(msg);
int err;
u_int16_t replylen;
if (!serv) {
msg->hdr.m_result = ENODEV;
msg->hdr.m_len = 0;
return;
}
err = ppp_getconnectdata(serv, reply, &replylen, 0);
if (err) {
msg->hdr.m_result = err;
msg->hdr.m_len = 0;
return;
}
msg->hdr.m_result = 0;
msg->hdr.m_len = replylen;
}
static
void socket_enable_event(struct client *client, struct msg *msg, void **reply)
{
u_int32_t notification = 1;
if (msg->hdr.m_len == 4) {
notification = *(u_int32_t *)&msg->data[MSG_DATAOFF(msg)];
if (client->flags & CLIENT_FLAG_SWAP_BYTES)
notification = htonl(notification);
if (notification < 1 || notification > 3) {
msg->hdr.m_result = EINVAL;
msg->hdr.m_len = 0;
return;
}
}
msg->hdr.m_result = 0;
client->flags &= ~(CLIENT_FLAG_NOTIFY_EVENT + CLIENT_FLAG_NOTIFY_STATUS);
if (notification & 1)
client->flags |= CLIENT_FLAG_NOTIFY_EVENT;
if (notification & 2)
client->flags |= CLIENT_FLAG_NOTIFY_STATUS;
client->notify_link = 0;
if (client->notify_serviceid) {
free(client->notify_serviceid);
client->notify_serviceid = 0;
}
if (msg->hdr.m_flags & USE_SERVICEID) {
if (client->notify_serviceid = malloc(msg->hdr.m_link + 1)) {
strncpy(client->notify_serviceid, msg->data, msg->hdr.m_link);
client->notify_serviceid[msg->hdr.m_link] = 0;
}
else
msg->hdr.m_result = ENOMEM;
}
else
client->notify_link = msg->hdr.m_link;
msg->hdr.m_len = 0;
}
static
void socket_disable_event(struct client *client, struct msg *msg, void **reply)
{
u_int32_t notification = 1;
if (msg->hdr.m_len == 4) {
notification = *(u_int32_t *)&msg->data[MSG_DATAOFF(msg)];
if (client->flags & CLIENT_FLAG_SWAP_BYTES)
notification = htonl(notification);
if (notification < 1 || notification > 3) {
msg->hdr.m_result = EINVAL;
msg->hdr.m_len = 0;
return;
}
}
if (notification & 1)
client->flags &= ~CLIENT_FLAG_NOTIFY_EVENT;
if (notification & 2)
client->flags &= ~CLIENT_FLAG_NOTIFY_STATUS;
if ((client->flags & (CLIENT_FLAG_NOTIFY_EVENT + CLIENT_FLAG_NOTIFY_EVENT)) == 0) {
client->notify_link = 0;
if (client->notify_serviceid) {
free(client->notify_serviceid);
client->notify_serviceid = 0;
}
}
msg->hdr.m_result = 0;
msg->hdr.m_len = 0;
}
static
void socket_version(struct client *client, struct msg *msg, void **reply)
{
*reply = my_Allocate(sizeof(u_int32_t));
if (*reply == 0) {
msg->hdr.m_result = ENOMEM;
msg->hdr.m_len = 0;
}
else {
msg->hdr.m_result = 0;
msg->hdr.m_len = sizeof(u_int32_t);
*(u_int32_t*)*reply = CURRENT_VERSION;
if (client->flags & CLIENT_FLAG_SWAP_BYTES)
*(u_int32_t*)*reply = htonl(*(u_int32_t*)*reply);
}
}
static
void socket_getnblinks(struct client *client, struct msg *msg, void **reply)
{
u_int32_t nb = 0;
struct service *serv;
u_short subtype = msg->hdr.m_link >> 16;
TAILQ_FOREACH(serv, &service_head, next) {
if ((subtype == 0xFFFF)
|| ( subtype == serv->subtype)) {
nb++;
}
}
*reply = my_Allocate(sizeof(u_int32_t));
if (*reply == 0) {
msg->hdr.m_result = ENOMEM;
msg->hdr.m_len = 0;
}
else {
msg->hdr.m_result = 0;
msg->hdr.m_len = sizeof(u_int32_t);
*(u_int32_t*)*reply = nb;
if (client->flags & CLIENT_FLAG_SWAP_BYTES)
*(u_int32_t*)*reply = htonl(*(u_int32_t*)*reply);
}
}
static
void socket_getlinkbyindex(struct client *client, struct msg *msg, void **reply)
{
u_int32_t nb = 0, len = 0, err = ENODEV, index;
struct service *serv;
u_short subtype = msg->hdr.m_link >> 16;
index = *(u_int32_t *)&msg->data[0];
if (client->flags & CLIENT_FLAG_SWAP_BYTES)
index = htonl(index);
TAILQ_FOREACH(serv, &service_head, next) {
if ((subtype == 0xFFFF)
|| (subtype == serv->subtype)) {
if (nb == index) {
*reply = my_Allocate(sizeof(u_int32_t));
if (*reply == 0)
err = ENOMEM;
else {
err = 0;
len = sizeof(u_int32_t);
*(u_int32_t*)*reply = makeref(serv);
if (client->flags & CLIENT_FLAG_SWAP_BYTES)
*(u_int32_t*)*reply = htonl(*(u_int32_t*)*reply);
}
break;
}
nb++;
}
}
msg->hdr.m_result = err;
msg->hdr.m_len = len;
}
static
void socket_getlinkbyserviceid(struct client *client, struct msg *msg, void **reply)
{
u_int32_t len = 0, err = ENODEV;
struct service *serv;
CFStringRef ref;
msg->data[msg->hdr.m_len] = 0;
ref = CFStringCreateWithCString(NULL, msg->data, kCFStringEncodingUTF8);
if (ref) {
serv = findbyserviceID(ref);
if (serv) {
*reply = my_Allocate(sizeof(u_int32_t));
if (*reply == 0)
err = ENOMEM;
else {
err = 0;
len = sizeof(u_int32_t);
*(u_int32_t*)*reply = makeref(serv);
if (client->flags & CLIENT_FLAG_SWAP_BYTES)
*(u_int32_t*)*reply = htonl(*(u_int32_t*)*reply);
}
}
CFRelease(ref);
}
else
err = ENOMEM;
msg->hdr.m_result = err;
msg->hdr.m_len = len;
}
static
void socket_getlinkbyifname(struct client *client, struct msg *msg, void **reply)
{
u_int32_t len = 0, err = ENODEV;
struct service *serv;
TAILQ_FOREACH(serv, &service_head, next) {
if (!strncmp(serv->u.ppp.ifname, &msg->data[0], sizeof(serv->u.ppp.ifname))) {
if (msg->hdr.m_flags & USE_SERVICEID) {
*reply = my_Allocate(strlen(serv->sid));
if (*reply == 0)
err = ENOMEM;
else {
err = 0;
len = strlen(serv->sid);
bcopy(serv->sid, *reply, len);
}
}
else {
*reply = my_Allocate(sizeof(u_int32_t));
if (*reply == 0)
err = ENOMEM;
else {
err = 0;
len = sizeof(u_int32_t);
*(u_int32_t*)*reply = makeref(serv);
if (client->flags & CLIENT_FLAG_SWAP_BYTES)
*(u_int32_t*)*reply = htonl(*(u_int32_t*)*reply);
}
}
break;
}
}
msg->hdr.m_result = err;
msg->hdr.m_len = len;
}
static
void socket_pppd_event(struct client *client, struct msg *msg)
{
struct service *serv = ppp_find(msg);
void *data = &msg->data[MSG_DATAOFF(msg)];
u_int32_t event = *(u_int32_t *)data;
u_int32_t error = *(u_int32_t *)(data + 4);
msg->hdr.m_len = 0xFFFFFFFF;
if (!serv)
return;
if (event == PPP_EVT_DISCONNECTED) {
error = ppp_translate_error(serv->subtype, error, 0);
}
else
error = 0;
client_notify(serv->serviceID, serv->sid, makeref(serv), event, error, CLIENT_FLAG_NOTIFY_EVENT, ppp_getstatus(serv));
}
static
void socket_pppd_status(struct client *client, struct msg *msg)
{
struct service *serv = ppp_find(msg);
void *data = &msg->data[MSG_DATAOFF(msg)];
u_int32_t status = *(u_int32_t *)data;
u_int32_t devstatus = *(u_int32_t *)(data + 4);
msg->hdr.m_len = 0xFFFFFFFF;
if (!serv)
return;
ppp_updatestatus(serv, status, devstatus);
}
static
void socket_pppd_phase(struct client *client, struct msg *msg)
{
struct service *serv = ppp_find(msg);
void *data = &msg->data[MSG_DATAOFF(msg)];
u_int32_t phase = *(u_int32_t *)data;
msg->hdr.m_len = 0xFFFFFFFF;
if (!serv)
return;
ppp_updatephase(serv, phase);
}
static
CFMutableDictionaryRef prepare_entity(CFMutableDictionaryRef opts, CFStringRef entity, CFStringRef property)
{
CFMutableDictionaryRef dict;
dict = (CFMutableDictionaryRef)CFDictionaryGetValue(opts, entity);
if (dict && (CFGetTypeID(dict) != CFDictionaryGetTypeID()))
return 0;
if (dict == 0) {
dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (dict == 0)
return 0;
CFDictionaryAddValue((CFMutableDictionaryRef)opts, entity, dict);
CFRelease(dict);
}
CFDictionaryRemoveValue(dict, property);
return dict;
}
static
int set_long_opt(CFMutableDictionaryRef opts, CFStringRef entity, CFStringRef property, u_long opt, u_long mini, u_long maxi, u_long limit)
{
CFMutableDictionaryRef dict;
CFNumberRef num;
if (opt < mini) {
if (limit) opt = mini;
else return EINVAL;
}
else if (opt > maxi) {
if (limit) opt = maxi;
else return EINVAL;
}
dict = prepare_entity(opts, entity, property);
if (dict == 0)
return ENOMEM;
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &opt);
if (num) {
CFDictionaryAddValue(dict, property, num);
CFRelease(num);
}
return 0;
}
static
int set_str_opt(CFMutableDictionaryRef opts, CFStringRef entity, CFStringRef property, char *opt, int len, CFStringRef optref)
{
CFMutableDictionaryRef dict;
CFStringRef str;
dict = prepare_entity(opts, entity, property);
if (dict == 0)
return ENOMEM;
if (optref)
CFDictionaryAddValue(dict, property, optref);
else {
opt[len] = 0;
str = CFStringCreateWithCString(NULL, opt, kCFStringEncodingUTF8);
if (str) {
CFDictionaryAddValue(dict, property, str);
CFRelease(str);
}
}
return 0;
}
static
int set_array_opt(CFMutableDictionaryRef opts, CFStringRef entity, CFStringRef property, CFStringRef optref1, CFStringRef optref2)
{
CFMutableDictionaryRef dict;
CFMutableArrayRef array;
dict = prepare_entity(opts, entity, property);
if (dict == 0)
return ENOMEM;
array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
if (array == 0)
return ENOMEM;
if (optref1)
CFArrayAppendValue(array, optref1);
if (optref2)
CFArrayAppendValue(array, optref2);
CFDictionaryAddValue(dict, property, array);
CFRelease(array);
return 0;
}
static
void remove_opt(CFMutableDictionaryRef opts, CFStringRef entity, CFStringRef property)
{
CFMutableDictionaryRef dict;
dict = (CFMutableDictionaryRef)CFDictionaryGetValue(opts, entity);
if (dict&& (CFGetTypeID(dict) == CFDictionaryGetTypeID()))
CFDictionaryRemoveValue(dict, property);
}
static
void socket_setoption(struct client *client, struct msg *msg, void **reply)
{
struct ppp_opt *opt = (struct ppp_opt *)&msg->data[MSG_DATAOFF(msg)];
u_int32_t optint = *(u_int32_t *)(&opt->o_data[0]);
u_char *optstr = &opt->o_data[0];
CFMutableDictionaryRef opts;
u_int32_t err = 0, len = msg->hdr.m_len - sizeof(struct ppp_opt_hdr), speed;
struct service *serv = ppp_find(msg);
CFStringRef string1, string2;
if (!serv) {
msg->hdr.m_result = ENODEV;
msg->hdr.m_len = 0;
return;
}
if (client->flags & CLIENT_FLAG_SWAP_BYTES) {
opt->o_type = htonl(opt->o_type);
optint = htonl(optint);
}
opts = client_findoptset(client, serv->serviceID);
if (!opts) {
opts = client_newoptset(client, serv->serviceID);
if (!opts) {
msg->hdr.m_result = ENOMEM;
msg->hdr.m_len = 0;
return;
}
}
switch (opt->o_type) {
case PPP_OPT_DEV_NAME:
err = set_str_opt(opts, kSCEntNetInterface, kSCPropNetInterfaceDeviceName, optstr, len, 0);
break;
case PPP_OPT_DEV_SPEED:
speed = optint;
if (speed <= 1200) speed = 1200;
else if ((speed > 1200) && (speed <= 2400)) speed = 2400;
else if ((speed > 2400) && (speed <= 9600)) speed = 9600;
else if ((speed > 9600) && (speed <= 19200)) speed = 19200;
else if ((speed > 19200) && (speed <= 38400)) speed = 38400;
else if ((speed > 38400) && (speed <= 57600)) speed = 57600;
else if ((speed > 38400) && (speed <= 57600)) speed = 57600;
else if ((speed > 57600) && (speed <= 0xFFFFFFFF)) speed = 115200;
err = set_long_opt(opts, kSCEntNetModem, kSCPropNetModemSpeed, speed, 0, 0xFFFFFFFF, 0);
break;
case PPP_OPT_DEV_CONNECTSCRIPT:
err = set_str_opt(opts, kSCEntNetModem, kSCPropNetModemConnectionScript, optstr, len, 0);
break;
case PPP_OPT_DEV_DIALMODE:
string1 = kSCValNetModemDialModeWaitForDialTone;
switch (optint) {
case PPP_DEV_IGNOREDIALTONE:
string1 = kSCValNetModemDialModeIgnoreDialTone;
break;
case PPP_DEV_MANUALDIAL:
string1 = kSCValNetModemDialModeManual;
break;
}
if (string1)
set_str_opt(opts, kSCEntNetModem, kSCPropNetModemDialMode, 0, 0, string1);
break;
case PPP_OPT_COMM_TERMINALMODE:
switch (optint) {
case PPP_COMM_TERM_NONE:
remove_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommDisplayTerminalWindow);
remove_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommUseTerminalScript);
break;
case PPP_COMM_TERM_SCRIPT:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommUseTerminalScript, 1, 0, 1, 1)
|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommDisplayTerminalWindow, 0, 0, 1, 1);
break;
case PPP_COMM_TERM_WINDOW:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommUseTerminalScript, 0, 0, 1, 1)
|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommDisplayTerminalWindow, 1, 0, 1, 1);
break;
}
break;
case PPP_OPT_COMM_TERMINALSCRIPT:
err = set_str_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommTerminalScript, optstr, len, 0);
break;
case PPP_OPT_COMM_REMOTEADDR:
err = set_str_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommRemoteAddress, optstr, len, 0);
break;
case PPP_OPT_COMM_IDLETIMER:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPDisconnectOnIdleTimer, optint, 0, 0xFFFFFFFF, 1)
|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPDisconnectOnIdle, optint, 0, 1, 1);
break;
case PPP_OPT_COMM_SESSIONTIMER:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPSessionTimer, optint, 0, 0xFFFFFFFF, 1)
|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPUseSessionTimer, optint, 0, 1, 1);
break;
case PPP_OPT_COMM_CONNECTDELAY:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommConnectDelay, optint, 0, 0xFFFFFFFF, 1);
break;
case PPP_OPT_LCP_HDRCOMP:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPCompressionPField, optint & PPP_LCP_HDRCOMP_PROTO, 0, 1, 1)
|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPCompressionACField, optint & PPP_LCP_HDRCOMP_ADDR, 0, 1, 1);
break;
case PPP_OPT_LCP_MRU:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPMRU, optint, 0, 0xFFFFFFFF, 1);
break;
case PPP_OPT_LCP_MTU:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPMTU, optint, 0, 0xFFFFFFFF, 1);
break;
case PPP_OPT_LCP_RCACCM:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPReceiveACCM, optint, 0, 0xFFFFFFFF, 1);
break;
case PPP_OPT_LCP_TXACCM:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPTransmitACCM, optint, 0, 0xFFFFFFFF, 1);
break;
case PPP_OPT_LCP_ECHO:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPEchoInterval, ((struct ppp_opt_echo *)opt->o_data)->interval, 0, 0xFFFFFFFF, 1)
|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPEchoFailure, ((struct ppp_opt_echo *)opt->o_data)->failure, 0, 0xFFFFFFFF, 1);
break;
case PPP_OPT_AUTH_PROTO:
string1 = string2 = 0;
switch (optint) {
case PPP_AUTH_NONE:
string1 = CFSTR("None"); break;
case PPP_AUTH_PAP:
string1 = kSCValNetPPPAuthProtocolPAP;
break;
case PPP_AUTH_CHAP:
string2 = kSCValNetPPPAuthProtocolCHAP;
break;
case PPP_AUTH_PAPCHAP:
string1 = kSCValNetPPPAuthProtocolPAP;
string2 = kSCValNetPPPAuthProtocolCHAP;
break;
default:
err = EINVAL;
}
if (string1 || string2)
err = set_array_opt(opts, kSCEntNetPPP, kSCPropNetPPPAuthProtocol, string1, string2);
break;
case PPP_OPT_AUTH_NAME:
err = set_str_opt(opts, kSCEntNetPPP, kSCPropNetPPPAuthName, optstr, len, 0);
break;
case PPP_OPT_AUTH_PASSWD:
err = set_str_opt(opts, kSCEntNetPPP, kSCPropNetPPPAuthPassword, optstr, len, 0);
break;
case PPP_OPT_IPCP_HDRCOMP:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPIPCPCompressionVJ, optint, 0, 1, 1);
break;
case PPP_OPT_IPCP_REMOTEADDR:
string1 = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d.%d.%d.%d"),
optint >> 24, (optint >> 16) & 0xFF, (optint >> 8) & 0xFF, optint & 0xFF);
if (string1) {
err = set_array_opt(opts, kSCEntNetIPv4, kSCPropNetIPv4DestAddresses, string1, 0);
CFRelease(string1);
}
break;
case PPP_OPT_IPCP_LOCALADDR:
string1 = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d.%d.%d.%d"),
optint >> 24, (optint >> 16) & 0xFF, (optint >> 8) & 0xFF, optint & 0xFF);
if (string1) {
err = set_array_opt(opts, kSCEntNetIPv4, kSCPropNetIPv4Addresses, string1, 0);
CFRelease(string1);
}
break;
case PPP_OPT_LOGFILE:
err = EOPNOTSUPP;
break;
case PPP_OPT_COMM_REMINDERTIMER:
err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPIdleReminderTimer, optint, 0, 0xFFFFFFFF, 1)
|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPIdleReminder, optint, 0, 1, 1);
break;
case PPP_OPT_ALERTENABLE:
err = set_long_opt(opts, kSCEntNetPPP, CFSTR("AlertEnable"), optint, 0, 0xFFFFFFFF, 1);
break;
default:
err = EOPNOTSUPP;
};
msg->hdr.m_result = err;
msg->hdr.m_len = 0;
}
static
void socket_getoption (struct client *client, struct msg *msg, void **reply)
{
struct ppp_opt *opt = (struct ppp_opt *)&msg->data[MSG_DATAOFF(msg)];
CFDictionaryRef opts;
struct service *serv = ppp_find(msg);
u_int8_t optdata[OPT_STR_LEN + 1];
u_int32_t optlen;
if (!serv) {
msg->hdr.m_len = 0;
msg->hdr.m_result = ENODEV;
return;
}
if (client->flags & CLIENT_FLAG_SWAP_BYTES)
opt->o_type = htonl(opt->o_type);
if (serv->u.ppp.phase != PPP_IDLE)
opts = serv->connectopts;
else
opts = client_findoptset(client, serv->serviceID);
if (!ppp_getoptval(serv, opts, 0, opt->o_type, optdata, &optlen)) {
msg->hdr.m_len = 0;
msg->hdr.m_result = EOPNOTSUPP;
return;
}
*reply = my_Allocate(sizeof(struct ppp_opt_hdr) + optlen);
if (*reply == 0) {
msg->hdr.m_result = ENOMEM;
msg->hdr.m_len = 0;
return;
}
if (client->flags & CLIENT_FLAG_SWAP_BYTES) {
switch(opt->o_type) {
case PPP_OPT_DEV_SPEED:
case PPP_OPT_COMM_IDLETIMER:
case PPP_OPT_AUTH_PROTO:
case PPP_OPT_LCP_HDRCOMP:
case PPP_OPT_LCP_MRU:
case PPP_OPT_LCP_MTU:
case PPP_OPT_LCP_RCACCM:
case PPP_OPT_LCP_TXACCM:
case PPP_OPT_IPCP_HDRCOMP:
case PPP_OPT_IPCP_LOCALADDR:
case PPP_OPT_IPCP_REMOTEADDR:
case PPP_OPT_RESERVED:
case PPP_OPT_COMM_REMINDERTIMER:
case PPP_OPT_ALERTENABLE:
case PPP_OPT_COMM_CONNECTDELAY:
case PPP_OPT_COMM_SESSIONTIMER:
case PPP_OPT_COMM_TERMINALMODE:
case PPP_OPT_DEV_CONNECTSPEED:
case PPP_OPT_DEV_DIALMODE:
case PPP_OPT_DIALONDEMAND:
case PPP_OPT_LCP_ECHO:
*(u_int32_t*)optdata = htonl(*(u_int32_t*)optdata);
break;
}
}
bcopy(opt, *reply, sizeof(struct ppp_opt_hdr));
bcopy(optdata, (*reply) + sizeof(struct ppp_opt_hdr), optlen);
msg->hdr.m_result = 0;
msg->hdr.m_len = sizeof(struct ppp_opt_hdr) + optlen;
}
void socket_client_notify (CFSocketRef ref, u_char *sid, u_int32_t link, u_long event, u_long error, u_int32_t flags)
{
struct ppp_msg_hdr msg;
int link_len = 0;
bzero(&msg, sizeof(msg));
msg.m_type = PPP_EVENT;
msg.m_link = link;
msg.m_result = event;
msg.m_cookie = error;
if (sid) {
msg.m_flags |= USE_SERVICEID;
msg.m_link = strlen(sid);
link_len = msg.m_link;
}
if (flags & CLIENT_FLAG_SWAP_BYTES) {
msg.m_flags = htons(msg.m_flags);
msg.m_type = htons(msg.m_type);
msg.m_result = htonl(msg.m_result);
msg.m_cookie = htonl(msg.m_cookie);
msg.m_link = htonl(msg.m_link);
msg.m_len = htonl(msg.m_len);
}
if (writen(CFSocketGetNative(ref), &msg, sizeof(msg)) != sizeof(msg))
return;
if (sid) {
if (writen(CFSocketGetNative(ref), sid, link_len) != link_len)
return;
}
}