1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/* -----------------------------------------------------------------------------
25includes
26----------------------------------------------------------------------------- */
27#include <string.h>
28#include <stdio.h>
29#include <sys/errno.h>
30#include <sys/signal.h>
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <sys/stat.h>
34#include <sys/un.h>
35#include <unistd.h>
36#include <sys/param.h>
37#include <sys/fcntl.h>
38#include <sys/ucred.h>
39#include <CoreFoundation/CoreFoundation.h>
40#include <SystemConfiguration/SystemConfiguration.h>
41
42#include <SystemConfiguration/SCPrivate.h>      // for SCLog()
43
44#include "ppp_msg.h"
45#include "scnc_main.h"
46#include "ppp_privmsg.h"
47#include "scnc_client.h"
48#include "ppp_manager.h"
49#include "ppp_option.h"
50#include "ppp_socket_server.h"
51#include "scnc_utils.h"
52
53/* -----------------------------------------------------------------------------
54definitions
55----------------------------------------------------------------------------- */
56
57enum {
58    do_nothing = 0,
59    do_process,
60    do_close,
61    do_error
62};
63
64
65/* -----------------------------------------------------------------------------
66forward declarations
67----------------------------------------------------------------------------- */
68
69static void socket_status (struct client *client, struct msg *msg, void **reply);
70static void socket_extendedstatus (struct client *client, struct msg *msg, void **reply);
71static void socket_connect (struct client *client, struct msg *msg, void **reply);
72static void socket_disconnect (struct client *client, struct msg *msg, void **reply);
73static void socket_suspend (struct client *client, struct msg *msg, void **reply);
74static void socket_resume (struct client *client, struct msg *msg, void **reply);
75static void socket_getconnectdata (struct client *client, struct msg *msg, void **reply);
76static void socket_enable_event (struct client *client, struct msg *msg, void **reply);
77static void socket_disable_event (struct client *client, struct msg *msg, void **reply);
78static void socket_version (struct client *client, struct msg *msg, void **reply);
79static void socket_getnblinks (struct client *client, struct msg *msg, void **reply);
80static void socket_getlinkbyindex (struct client *client, struct msg *msg, void **reply);
81static void socket_getlinkbyserviceid (struct client *client, struct msg *msg, void **reply);
82static void socket_getlinkbyifname (struct client *client, struct msg *msg, void **reply);
83static void socket_setoption (struct client *client, struct msg *msg, void **reply);
84static void socket_getoption (struct client *client, struct msg *msg, void **reply);
85
86static void socket_pppd_event(struct client *client, struct msg *msg);
87static void socket_pppd_status(struct client *client, struct msg *msg);
88static void socket_pppd_phase(struct client *client, struct msg *msg);
89
90
91
92static void processRequest (struct client *client, struct msg *msg);
93
94static void listenCallBack(CFSocketRef s, CFSocketCallBackType type,
95                     CFDataRef address, const void *data, void *info);
96static void clientCallBack(CFSocketRef s, CFSocketCallBackType type,
97                     CFDataRef address, const void *data, void *info);
98
99
100
101/* -----------------------------------------------------------------------------
102globals
103----------------------------------------------------------------------------- */
104
105extern TAILQ_HEAD(, service) 	service_head;
106
107/* -----------------------------------------------------------------------------
108----------------------------------------------------------------------------- */
109int ppp_socket_start_server ()
110{
111    struct sockaddr_un	addr;
112    int			error, s;
113    mode_t		mask;
114    CFSocketRef		ref = 0;
115    CFRunLoopSourceRef	rls;
116    CFSocketContext	context = { 0, NULL, NULL, NULL, NULL };
117
118    if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1)
119        goto fail;
120
121    unlink(PPP_PATH);
122    bzero(&addr, sizeof(addr));
123    addr.sun_family = AF_LOCAL;
124    strlcpy(addr.sun_path, PPP_PATH, sizeof(addr.sun_path));
125    mask = umask(0);
126    error = bind(s, (struct sockaddr *)&addr, SUN_LEN(&addr));
127    umask(mask);
128    if (error)
129        goto fail;
130
131    if ((ref = CFSocketCreateWithNative(NULL, s, kCFSocketReadCallBack,
132                                   listenCallBack, &context)) == 0)
133        goto fail;
134
135    if ((rls = CFSocketCreateRunLoopSource(NULL, ref, 0)) == 0)
136        goto fail;
137
138    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
139    CFRelease(rls);
140
141    listen(s, SOMAXCONN);
142    CFRelease(ref);
143    return 0;
144
145fail:
146    SCLog(TRUE, LOG_INFO, CFSTR("PPPController: initialization failed..."));
147    if (s != -1)
148        close(s);
149    if (ref) {
150        CFSocketInvalidate(ref);
151        CFRelease(ref);
152    }
153    return 1;
154}
155
156/* -----------------------------------------------------------------------------
157----------------------------------------------------------------------------- */
158int ppp_socket_create_client(int s, int priviledged, uid_t uid, gid_t gid)
159{
160    int			flags;
161    CFSocketRef		ref;
162    CFRunLoopSourceRef	rls;
163    CFSocketContext	context = { 0, NULL, NULL, NULL, NULL };
164
165    if ((flags = fcntl(s, F_GETFL)) == -1
166	|| fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) {
167        SCLog(TRUE, LOG_INFO, CFSTR("Couldn't set client socket in non-blocking mode, errno = %d."), errno);
168    }
169
170    if ((ref = CFSocketCreateWithNative(NULL, s,
171                    kCFSocketReadCallBack, clientCallBack, &context)) == 0) {
172        close(s);
173        return -1;
174    }
175    if ((rls = CFSocketCreateRunLoopSource(NULL, ref, 0)) == 0)
176        goto fail;
177
178    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
179    CFRelease(rls);
180
181    if (client_new_socket(ref, priviledged, uid, gid) == 0)
182        goto fail;
183
184    CFRelease(ref);
185    return 0;
186
187fail:
188    CFSocketInvalidate(ref);
189    CFRelease(ref);
190    return -1;
191}
192
193/* -----------------------------------------------------------------------------
194----------------------------------------------------------------------------- */
195static
196void listenCallBack(CFSocketRef inref, CFSocketCallBackType type,
197                     CFDataRef address, const void *data, void *info)
198{
199    struct sockaddr_un	addr;
200    int			s;
201	uint32_t    len;
202	struct xucred xucred;
203
204    len = sizeof(addr);
205    if ((s = accept(CFSocketGetNative(inref), (struct sockaddr *) &addr, &len)) == -1)
206        return;
207
208    PRINTF(("Accepted connection...\n"));
209
210	len = sizeof(xucred);
211	if (getsockopt(s, 0, LOCAL_PEERCRED, &xucred, &len) == -1) {
212        SCLog(TRUE, LOG_ERR, CFSTR("PPPController: can't get LOCAL_PEERCRED, errno = %d."), errno);
213		return ;
214	}
215
216	ppp_socket_create_client(s, 0, 0 /*xucred.cr_uid*/, 0 /*xucred.cr_gid*/);
217}
218
219/* -----------------------------------------------------------------------------
220----------------------------------------------------------------------------- */
221int readn(int ref, void *data, int len)
222{
223#define MAXSLEEPTIME   40000    /* 1/25 of a second */
224#define MAXRETRY       10
225
226    int 	n, left = len;
227    void 	*p = data;
228    int     retry = MAXRETRY;
229
230   while (left > 0) {
231        if ((n = read(ref, p, left)) < 0) {
232            SCLog(TRUE, LOG_ERR, CFSTR("PPPController: readn, retry %d, errno %d."), retry, errno);
233            if (errno == EAGAIN){
234                if (retry--){
235                    if (!usleep(MAXSLEEPTIME))
236                        continue;
237                }else
238                    return (len-left);
239           }
240            if (errno != EINTR)
241                return -1;
242            n = 0;
243        }
244        else if (n == 0)
245            return -1; /* EOF */
246
247        left -= n;
248        p += n;
249    }
250    return (len - left);
251}
252
253/* -----------------------------------------------------------------------------
254----------------------------------------------------------------------------- */
255//static
256int writen(int ref, void *data, int len)
257{
258#define MAXSLEEPTIME   40000    /* 1/25 of a second */
259#define MAXRETRY       10
260    int 	n, left = len;
261    void 	*p = data;
262    int     retry = MAXRETRY;
263
264   while (left > 0) {
265        if ((n = write(ref, p, left)) <= 0) {
266            SCLog(TRUE, LOG_ERR, CFSTR("PPPController writen: retry %d, errno %d."), retry, errno);
267            if (errno == EAGAIN){
268                if (retry--){
269                    if (!usleep(MAXSLEEPTIME))
270                        continue;
271                }else
272                    return(len-left);
273                }
274            if (errno != EINTR)
275                return -1;
276            n = 0;
277        }
278        left -= n;
279        p += n;
280    }
281    return len;
282}
283
284typedef void (*msg_function)(struct client *client, struct msg *msg, void **reply);
285
286msg_function requests[] = {
287    NULL,			/* */
288    socket_version, 		/* PPP_VERSION */
289    socket_status, 		/* PPP_STATUS */
290    socket_connect, 		/* PPP_CONNECT */
291    NULL,			/* */
292    socket_disconnect, 		/* PPP_DISCONNECT */
293    socket_getoption, 		/* PPP_GETOPTION */
294    socket_setoption, 		/* PPP_SETOPTION */
295    socket_enable_event, 		/* PPP_ENABLE_EVENT */
296    socket_disable_event,		/* PPP_DISABLE_EVENT */
297    NULL,	 		/* PPP_EVENT */
298    socket_getnblinks, 		/* PPP_GETNBLINKS */
299    socket_getlinkbyindex, 	/* PPP_GETLINKBYINDEX */
300    socket_getlinkbyserviceid, 	/* PPP_GETLINKBYSERVICEID */
301    socket_getlinkbyifname, 	/* PPP_GETLINKBYIFNAME */
302    socket_suspend, 		/* PPP_SUSPEND */
303    socket_resume, 		/* PPP_RESUME */
304    socket_extendedstatus, 	/* PPP_EXTENDEDSTATUS */
305    socket_getconnectdata 		/* PPP_GETCONNECTDATA */
306};
307#define LAST_REQUEST PPP_GETCONNECTDATA
308
309/* -----------------------------------------------------------------------------
310----------------------------------------------------------------------------- */
311static
312void clientCallBack(CFSocketRef inref, CFSocketCallBackType type,
313                     CFDataRef address, const void *data, void *info)
314{
315    int 		s = CFSocketGetNative(inref);
316    int			action = do_nothing;
317    ssize_t		n;
318    struct client 	*client;
319
320    client = client_findbysocketref(inref);
321    if (client == 0)
322        return;
323
324    /* first read the header part of the message */
325    if (client->msglen < sizeof(struct ppp_msg_hdr)) {
326        n = readn(s, &((u_int8_t *)&client->msghdr)[client->msglen], sizeof(struct ppp_msg_hdr) - client->msglen);
327        switch (n) {
328            case -1:
329                action = do_close;
330				goto clientCallBackPerformAction;
331            default:
332                client->msglen += n;
333                if (client->msglen == sizeof(struct ppp_msg_hdr)) {
334
335					/* check if message bytes are in network order */
336					if (!(client->flags & CLIENT_FLAG_PRIVILEDGED) && (client->msghdr.m_type > LAST_REQUEST)) {
337						client->flags |= CLIENT_FLAG_SWAP_BYTES;
338						client->msghdr.m_flags = ntohs(client->msghdr.m_flags);
339						client->msghdr.m_type = ntohs(client->msghdr.m_type);
340						client->msghdr.m_result = ntohl(client->msghdr.m_result);
341						client->msghdr.m_cookie = ntohl(client->msghdr.m_cookie);
342						client->msghdr.m_link = ntohl(client->msghdr.m_link);
343						client->msghdr.m_len = ntohl(client->msghdr.m_len);
344					}
345					else
346						client->flags &= ~CLIENT_FLAG_SWAP_BYTES;
347
348					/* verify msghdr fields that are used to calculate msgtotallen */
349					if (client->msghdr.m_len > PPP_MSG_MAX_DATA_LEN) {
350						SCLog(TRUE, LOG_ERR, CFSTR("Invalid client message header: length %d..."), client->msghdr.m_len);
351						action = do_error;
352						goto clientCallBackPerformAction;
353					}
354					if (client->msghdr.m_flags & USE_SERVICEID &&
355						client->msghdr.m_link > PPP_MSG_MAX_SERVICEID_LEN) {
356						SCLog(TRUE, LOG_ERR, CFSTR("Invalid client message header: service-id %d..."), client->msghdr.m_link);
357						action = do_error;
358						goto clientCallBackPerformAction;
359					}
360
361                    client->msgtotallen = client->msglen
362                        + client->msghdr.m_len
363                        + (client->msghdr.m_flags & USE_SERVICEID ? client->msghdr.m_link : 0);
364                    client->msg = my_Allocate(client->msgtotallen + 1);
365					if (client->msg == 0) {
366						SCLog(TRUE, LOG_ERR, CFSTR("Failed to allocate client message..."));
367                        action = do_error;
368						goto clientCallBackPerformAction;
369                    } else {
370                        bcopy(&client->msghdr, client->msg, sizeof(struct ppp_msg_hdr));
371                        // let's end the message with a null byte
372                        client->msg[client->msgtotallen] = 0;
373                    }
374                }
375        }
376	}
377
378    /* first read the data part of the message, including serviceid */
379    if (client->msglen >= sizeof(struct ppp_msg_hdr)) {
380        n = readn(s, &client->msg[client->msglen], client->msgtotallen - client->msglen);
381        switch (n) {
382            case -1:
383				SCLog(TRUE, LOG_ERR, CFSTR("Failed to read client message..."));
384                action = do_close;
385                break;
386            default:
387                client->msglen += n;
388                if (client->msglen == client->msgtotallen) {
389                    action = do_process;
390                }
391        }
392    }
393
394clientCallBackPerformAction:
395    /* perform action */
396    switch (action) {
397        case do_nothing:
398            break;
399        case do_error:
400        case do_close:
401            PRINTF(("Connection closed...\n"));
402            /* connection closed by client */
403            client_dispose(client);
404            break;
405
406        case do_process:
407            // process client request
408            processRequest(client, ALIGNED_CAST(struct msg *)client->msg);
409            my_Deallocate(client->msg, client->msgtotallen + 1);
410            client->msg = 0;
411            client->msglen = 0;
412            client->msgtotallen = 0;
413            break;
414    }
415}
416
417/* -----------------------------------------------------------------------------
418----------------------------------------------------------------------------- */
419static
420void processRequest (struct client *client, struct msg *msg)
421{
422    void		*reply = 0;
423    msg_function	func;
424	struct ppp_msg_hdr	hdr;
425
426    PRINTF(("process_request : type = %x, len = %d\n", msg->hdr.m_type, msg->hdr.m_len));
427    //printf("process_request : type = %x, len = %d\n", msg->hdr.m_type, msg->hdr.m_len);
428
429    if (msg->hdr.m_type <= LAST_REQUEST) {
430
431        func = requests[msg->hdr.m_type];
432        if (func)
433            (*func)(client, msg, &reply);
434    }
435    else {
436        // check if it belongs to a controlling service
437        if (client->flags & CLIENT_FLAG_PRIVILEDGED) {
438            switch (msg->hdr.m_type) {
439                // private pppd event
440                case PPPD_EVENT:
441                    socket_pppd_event(client, msg);
442                    break;
443                case PPPD_STATUS:
444                    socket_pppd_status(client, msg);
445                    break;
446                case PPPD_PHASE:
447                    socket_pppd_phase(client, msg);
448                    break;
449            }
450        }
451    }
452
453	/* save header before swapping bytes */
454	bcopy(msg, &hdr, sizeof(hdr));
455
456	/* swap back bytes in the message header */
457	if (client->flags & CLIENT_FLAG_SWAP_BYTES) {
458		msg->hdr.m_flags = htons(msg->hdr.m_flags);
459		msg->hdr.m_type = htons(msg->hdr.m_type);
460		msg->hdr.m_result = htonl(msg->hdr.m_result);
461		msg->hdr.m_cookie = htonl(msg->hdr.m_cookie);
462		msg->hdr.m_link = htonl(msg->hdr.m_link);
463		msg->hdr.m_len = htonl(msg->hdr.m_len);
464	}
465
466    if (hdr.m_len != 0xFFFFFFFF) {
467
468        writen(CFSocketGetNative(client->socketRef), msg, sizeof(struct ppp_msg_hdr) +
469            (hdr.m_flags & USE_SERVICEID ? hdr.m_link : 0));
470
471       if (hdr.m_len) {
472            writen(CFSocketGetNative(client->socketRef), reply, hdr.m_len);
473            my_Deallocate(reply, hdr.m_len);
474        }
475        PRINTF(("process_request : m_type = 0x%x, result = 0x%x, cookie = 0x%x, link = 0x%x, len = 0x%x\n",
476                hdr.m_type, hdr.m_result, hdr.m_cookie, hdr.m_link, hdr.m_len));
477#if 0
478        if (hdr.m_type == PPP_STATUS) {
479            struct ppp_status *stat = (struct ppp_status *)&msg->data[0];
480            PRINTF(("     ----- status = 0x%x", stat->status));
481            if (stat->status != PPP_RUNNING) {
482                PRINTF((", cause = 0x%x", stat->s.disc.lastDiscCause));
483            }
484            PRINTF(("\n"));
485        }
486#endif
487    }
488}
489
490/* -----------------------------------------------------------------------------
491find the ppp structure corresponding to the message
492----------------------------------------------------------------------------- */
493struct service *ppp_find(struct msg *msg)
494{
495
496    if (msg->hdr.m_flags & USE_SERVICEID)
497        return findbysid(msg->data, msg->hdr.m_link);
498    else
499        return findbyref(TYPE_PPP, msg->hdr.m_link);
500
501    return 0;
502}
503
504/* -----------------------------------------------------------------------------
505----------------------------------------------------------------------------- */
506static
507void socket_status(struct client *client, struct msg *msg, void **reply)
508{
509    struct service			*serv = ppp_find(msg);
510    int					err;
511	u_int16_t			replylen;
512
513    if (!serv) {
514        msg->hdr.m_result = ENODEV;
515        msg->hdr.m_len = 0;
516        return;
517    }
518
519	err = ppp_getstatus1(serv, reply, &replylen);
520	if (err) {
521		msg->hdr.m_result = err;
522		msg->hdr.m_len = 0;
523        return;
524	}
525
526	if (client->flags & CLIENT_FLAG_SWAP_BYTES) {
527		struct ppp_status *stat = (struct ppp_status *)*reply;
528		stat->status = htonl(stat->status);
529		stat->s.run.timeElapsed = htonl(stat->s.run.timeElapsed);
530		stat->s.run.timeRemaining = htonl(stat->s.run.timeRemaining);
531		stat->s.run.inBytes = htonl(stat->s.run.inBytes);
532		stat->s.run.inPackets = htonl(stat->s.run.inPackets);
533		stat->s.run.inErrors = htonl(stat->s.run.inErrors);
534		stat->s.run.outBytes = htonl(stat->s.run.outBytes);
535		stat->s.run.outPackets = htonl(stat->s.run.outPackets);
536		stat->s.run.outErrors = htonl(stat->s.run.outErrors);
537	}
538
539    msg->hdr.m_result = 0;
540    msg->hdr.m_len = replylen;
541}
542
543/* -----------------------------------------------------------------------------
544----------------------------------------------------------------------------- */
545static
546void socket_extendedstatus(struct client *client, struct msg *msg, void **reply)
547{
548    struct service			*serv = ppp_find(msg);
549	int					err = 0;
550	uint32_t			replylen = 0;
551	CFDictionaryRef		status = NULL;
552	CFDataRef			data = NULL;
553
554	if (!serv) {
555		err = ENODEV;
556		goto done;
557	}
558
559	err = ppp_copyextendedstatus(serv, &status);
560	if (err) {
561		goto done;
562	}
563
564	if (status != NULL) {
565		void *dataptr = NULL;
566
567		data = Serialize(status, &dataptr, &replylen);
568		if (data == NULL) {
569			err = ENOMEM;
570			goto done;
571		}
572
573		*reply = my_Allocate(replylen);
574		if (*reply == NULL) {
575			err = ENOMEM;
576			goto done;
577		}
578
579		bcopy(dataptr, *reply, replylen);
580	}
581
582done:
583	msg->hdr.m_result = err;
584	msg->hdr.m_len = (err == 0 ? (uint16_t)replylen : 0);
585
586	if (status != NULL) {
587		CFRelease(status);
588	}
589	if (data != NULL) {
590		CFRelease(data);
591	}
592}
593
594/* -----------------------------------------------------------------------------
595----------------------------------------------------------------------------- */
596static
597void socket_connect(struct client *client, struct msg *msg, void **reply)
598{
599    void			*data = &msg->data[MSG_DATAOFF(msg)];
600    struct service	*serv = ppp_find(msg);
601    CFDictionaryRef	opts = 0;
602
603    if (!serv) {
604        msg->hdr.m_result = ENODEV;
605        msg->hdr.m_len = 0;
606        return;
607    }
608
609    if (msg->hdr.m_len == 0) {
610        // first find current the appropriate set of options
611        opts = client_findoptset(client, serv->serviceID);
612    }
613    else {
614        opts = (CFDictionaryRef)Unserialize(data, msg->hdr.m_len);
615        if (opts == 0 || CFGetTypeID(opts) != CFDictionaryGetTypeID()) {
616            msg->hdr.m_result = ENOMEM;
617            msg->hdr.m_len = 0;
618            if (opts)
619                CFRelease(opts);
620            return;
621        }
622    }
623
624    msg->hdr.m_result = scnc_start(serv, opts,
625        (msg->hdr.m_flags & CONNECT_ARBITRATED_FLAG) ? client : 0,
626        (msg->hdr.m_flags & CONNECT_AUTOCLOSE_FLAG) ? 1 : 0, client->uid, client->gid,
627        client->pid, 0, 0);
628    if (opts && msg->hdr.m_len)
629        CFRelease(opts);
630    msg->hdr.m_len = 0;
631}
632
633/* -----------------------------------------------------------------------------
634----------------------------------------------------------------------------- */
635static
636void socket_disconnect(struct client *client, struct msg *msg, void **reply)
637{
638    struct service		*serv = ppp_find(msg);
639	struct client       *arb_client;
640	int                  scnc_reason;
641
642    if (!serv) {
643        msg->hdr.m_result = ENODEV;
644        msg->hdr.m_len = 0;
645        return;
646    }
647    arb_client = (msg->hdr.m_flags & DISCONNECT_ARBITRATED_FLAG) ? client : 0;
648	scnc_reason = arb_client? SCNC_STOP_SOCK_DISCONNECT : SCNC_STOP_SOCK_DISCONNECT_NO_CLIENT;
649    scnc_stop(serv, client, SIGHUP, scnc_reason);
650
651    msg->hdr.m_result = 0;
652    msg->hdr.m_len = 0;
653}
654
655/* -----------------------------------------------------------------------------
656----------------------------------------------------------------------------- */
657static
658void socket_suspend(struct client *client, struct msg *msg, void **reply)
659{
660    struct service		*serv = ppp_find(msg);
661
662    if (!serv) {
663        msg->hdr.m_result = ENODEV;
664        msg->hdr.m_len = 0;
665        return;
666    }
667
668    ppp_suspend(serv);
669
670    msg->hdr.m_result = 0;
671    msg->hdr.m_len = 0;
672}
673
674/* -----------------------------------------------------------------------------
675----------------------------------------------------------------------------- */
676static
677void socket_resume(struct client *client, struct msg *msg, void **reply)
678{
679    struct service		*serv = ppp_find(msg);
680
681    if (!serv) {
682        msg->hdr.m_result = ENODEV;
683        msg->hdr.m_len = 0;
684        return;
685    }
686
687    ppp_resume(serv);
688
689    msg->hdr.m_result = 0;
690    msg->hdr.m_len = 0;
691}
692
693/* -----------------------------------------------------------------------------
694----------------------------------------------------------------------------- */
695static
696void socket_getconnectdata(struct client *client, struct msg *msg, void **reply)
697{
698	struct service		*serv = ppp_find(msg);
699	int					err = 0;
700	uint32_t			replylen = 0;
701	CFDictionaryRef		userOptions = NULL;
702	CFDataRef			data = NULL;
703
704	if (!serv) {
705		err = ENODEV;
706		goto done;
707	}
708
709	err = ppp_getconnectdata(serv, &userOptions, 0);
710	if (err) {
711		goto done;
712	}
713
714	if (userOptions != NULL) {
715		void *dataptr = NULL;
716
717		data = Serialize(userOptions, &dataptr, &replylen);
718		if (data == NULL) {
719			err = ENOMEM;
720			goto done;
721		}
722
723		*reply = my_Allocate(replylen);
724		if (*reply == NULL) {
725			err = ENOMEM;
726			goto done;
727		}
728
729		bcopy(dataptr, *reply, replylen);
730	}
731
732done:
733	msg->hdr.m_result = err;
734	msg->hdr.m_len = (err == 0 ? (uint16_t)replylen : 0);
735
736	if (userOptions != NULL) {
737		CFRelease(userOptions);
738	}
739	if (data != NULL) {
740		CFRelease(data);
741	}
742}
743
744/* -----------------------------------------------------------------------------
745----------------------------------------------------------------------------- */
746static
747void socket_enable_event(struct client *client, struct msg *msg, void **reply)
748{
749    u_int32_t	notification = 1; // type of notification, event or status
750
751    if (msg->hdr.m_len == 4) {
752        notification = *ALIGNED_CAST(u_int32_t *)&msg->data[MSG_DATAOFF(msg)];
753		if (client->flags & CLIENT_FLAG_SWAP_BYTES)
754			notification = htonl(notification);
755        if (notification < 1 || notification > 3) {
756            msg->hdr.m_result = EINVAL;
757            msg->hdr.m_len = 0;
758            return;
759        }
760    }
761
762    msg->hdr.m_result = 0;
763	client->flags &= ~(CLIENT_FLAG_NOTIFY_EVENT + CLIENT_FLAG_NOTIFY_STATUS);
764	if (notification & 1)
765		client->flags |= CLIENT_FLAG_NOTIFY_EVENT;
766	if (notification & 2)
767		client->flags |= CLIENT_FLAG_NOTIFY_STATUS;
768    client->notify_link = 0;
769	if (client->notify_serviceid) {
770		free(client->notify_serviceid);
771		client->notify_serviceid = 0;
772	}
773    if (msg->hdr.m_flags & USE_SERVICEID) {
774        if ((client->notify_serviceid = malloc(msg->hdr.m_link + 1))) {
775            strncpy((char*)client->notify_serviceid, (char*)msg->data, msg->hdr.m_link);
776            client->notify_serviceid[msg->hdr.m_link] = 0;
777        }
778        else
779            msg->hdr.m_result = ENOMEM;
780    }
781    else
782        client->notify_link = msg->hdr.m_link;
783
784    msg->hdr.m_len = 0;
785}
786
787/* -----------------------------------------------------------------------------
788----------------------------------------------------------------------------- */
789static
790void socket_disable_event(struct client *client, struct msg *msg, void **reply)
791{
792    u_int32_t	notification = 1; // type of notification, event or status
793
794    if (msg->hdr.m_len == 4) {
795        notification = *ALIGNED_CAST(u_int32_t *)&msg->data[MSG_DATAOFF(msg)];
796		if (client->flags & CLIENT_FLAG_SWAP_BYTES)
797			notification = htonl(notification);
798        if (notification < 1 || notification > 3) {
799            msg->hdr.m_result = EINVAL;
800            msg->hdr.m_len = 0;
801            return;
802        }
803    }
804
805	if (notification & 1)
806		client->flags &= ~CLIENT_FLAG_NOTIFY_EVENT;
807	if (notification & 2)
808		client->flags &= ~CLIENT_FLAG_NOTIFY_STATUS;
809
810    if ((client->flags & (CLIENT_FLAG_NOTIFY_EVENT + CLIENT_FLAG_NOTIFY_EVENT)) == 0) {
811        client->notify_link = 0;
812        if (client->notify_serviceid) {
813            free(client->notify_serviceid);
814            client->notify_serviceid = 0;
815        }
816    }
817    msg->hdr.m_result = 0;
818    msg->hdr.m_len = 0;
819}
820
821/* -----------------------------------------------------------------------------
822----------------------------------------------------------------------------- */
823static
824void socket_version(struct client *client, struct msg *msg, void **reply)
825{
826
827    *reply = my_Allocate(sizeof(u_int32_t));
828    if (*reply == 0) {
829        msg->hdr.m_result = ENOMEM;
830        msg->hdr.m_len = 0;
831    }
832    else {
833        msg->hdr.m_result = 0;
834        msg->hdr.m_len = sizeof(u_int32_t);
835        *(u_int32_t*)*reply = CURRENT_VERSION;
836		if (client->flags & CLIENT_FLAG_SWAP_BYTES)
837			*(u_int32_t*)*reply = htonl(*(u_int32_t*)*reply);
838    }
839}
840
841/* -----------------------------------------------------------------------------
842----------------------------------------------------------------------------- */
843static
844void socket_getnblinks(struct client *client, struct msg *msg, void **reply)
845{
846    u_int32_t		nb = 0;
847    struct service	*serv;
848    u_short			subtype = msg->hdr.m_link >> 16;
849
850    TAILQ_FOREACH(serv, &service_head, next) {
851        if ((subtype == 0xFFFF)
852            || ( subtype == serv->subtype)) {
853            nb++;
854        }
855    }
856
857    *reply = my_Allocate(sizeof(u_int32_t));
858    if (*reply == 0) {
859        msg->hdr.m_result = ENOMEM;
860        msg->hdr.m_len = 0;
861    }
862    else {
863        msg->hdr.m_result = 0;
864        msg->hdr.m_len = sizeof(u_int32_t);
865        *(u_int32_t*)*reply = nb;
866		if (client->flags & CLIENT_FLAG_SWAP_BYTES)
867			*(u_int32_t*)*reply = htonl(*(u_int32_t*)*reply);
868    }
869}
870
871/* -----------------------------------------------------------------------------
872index is a global index across all the link types (or within the family)
873index if between 0 and nblinks
874----------------------------------------------------------------------------- */
875static
876void socket_getlinkbyindex(struct client *client, struct msg *msg, void **reply)
877{
878    u_int32_t		nb = 0, len = 0, err = ENODEV, index;
879    struct service	*serv;
880    u_short			subtype = msg->hdr.m_link >> 16;
881
882    index = *ALIGNED_CAST(u_int32_t *)&msg->data[0];
883	if (client->flags & CLIENT_FLAG_SWAP_BYTES)
884		index = htonl(index);
885
886    TAILQ_FOREACH(serv, &service_head, next) {
887        if ((subtype == 0xFFFF)
888            || (subtype == serv->subtype)) {
889            if (nb == index) {
890                *reply = my_Allocate(sizeof(u_int32_t));
891                if (*reply == 0)
892                    err = ENOMEM;
893                else {
894                    err = 0;
895                    len = sizeof(u_int32_t);
896                    *(u_int32_t*)*reply = makeref(serv);
897					if (client->flags & CLIENT_FLAG_SWAP_BYTES)
898						*(u_int32_t*)*reply = htonl(*(u_int32_t*)*reply);
899                }
900                break;
901            }
902            nb++;
903        }
904    }
905
906    msg->hdr.m_result = err;
907    msg->hdr.m_len = len;
908}
909
910/* -----------------------------------------------------------------------------
911----------------------------------------------------------------------------- */
912static
913void socket_getlinkbyserviceid(struct client *client, struct msg *msg, void **reply)
914{
915    u_int32_t		len = 0, err = ENODEV;
916    struct service	*serv;
917    CFStringRef		ref;
918
919    msg->data[msg->hdr.m_len] = 0;
920    ref = CFStringCreateWithCString(NULL, (char*)msg->data, kCFStringEncodingUTF8);
921    if (ref) {
922	serv = findbyserviceID(ref);
923        if (serv) {
924            *reply = my_Allocate(sizeof(u_int32_t));
925            if (*reply == 0)
926                err = ENOMEM;
927            else {
928                err = 0;
929                len = sizeof(u_int32_t);
930                *(u_int32_t*)*reply = makeref(serv);
931				if (client->flags & CLIENT_FLAG_SWAP_BYTES)
932					*(u_int32_t*)*reply = htonl(*(u_int32_t*)*reply);
933            }
934        }
935        CFRelease(ref);
936    }
937    else
938        err = ENOMEM;
939
940    msg->hdr.m_result = err;
941    msg->hdr.m_len = len;
942}
943
944/* -----------------------------------------------------------------------------
945----------------------------------------------------------------------------- */
946static
947void socket_getlinkbyifname(struct client *client, struct msg *msg, void **reply)
948{
949    u_int32_t		len = 0, err = ENODEV;
950    struct service	*serv;
951
952    TAILQ_FOREACH(serv, &service_head, next) {
953        if (!strncmp((char*)serv->if_name, (char*)&msg->data[0], sizeof(serv->if_name))) {
954
955            if (msg->hdr.m_flags & USE_SERVICEID) {
956                *reply = my_Allocate(strlen((char*)serv->sid));
957                if (*reply == 0)
958                    err = ENOMEM;
959                else {
960                    err = 0;
961                    len = strlen((char*)serv->sid);
962                    bcopy(serv->sid, *reply, len);
963                }
964            }
965            else {
966                *reply = my_Allocate(sizeof(u_int32_t));
967                if (*reply == 0)
968                    err = ENOMEM;
969                else {
970                    err = 0;
971                    len = sizeof(u_int32_t);
972                    *(u_int32_t*)*reply = makeref(serv);
973					if (client->flags & CLIENT_FLAG_SWAP_BYTES)
974						*(u_int32_t*)*reply = htonl(*(u_int32_t*)*reply);
975                }
976            }
977            break;
978        }
979    }
980
981    msg->hdr.m_result = err;
982    msg->hdr.m_len = len;
983}
984
985/* -----------------------------------------------------------------------------
986----------------------------------------------------------------------------- */
987static
988void socket_pppd_event(struct client *client, struct msg *msg)
989{
990    struct service		*serv = ppp_find(msg);
991    void 		*data = &msg->data[MSG_DATAOFF(msg)];
992    u_int32_t		event = *(u_int32_t *)data;
993    u_int32_t		error = *(u_int32_t *)(data + 4);
994
995    msg->hdr.m_len = 0xFFFFFFFF; // no reply
996    //printf("ppp_event, event = 0x%x, cause = 0x%x, serviceid = '%s'\n", event, error, serviceid);
997    if (!serv)
998        return;
999
1000	if (event == PPP_EVT_DISCONNECTED) {
1001		//if (error == EXIT_USER_REQUEST)
1002		//    return;	// PPP API generates PPP_EVT_DISCONNECTED only for unrequested disconnections
1003		error = ppp_translate_error(serv->subtype, error, 0);
1004		if (serv->ne_sm_bridge != NULL) {
1005			ne_sm_bridge_request_uninstall(serv->ne_sm_bridge);
1006		}
1007	} else if (event == PPP_EVT_REQUEST_INSTALL) {
1008		if (serv->ne_sm_bridge != NULL) {
1009			// error is used as a flag for exclusivity
1010			ne_sm_bridge_request_install(serv->ne_sm_bridge, error);
1011		}
1012	} else if (event == PPP_EVT_REQUEST_UNINSTALL) {
1013		if (serv->ne_sm_bridge != NULL) {
1014			ne_sm_bridge_request_uninstall(serv->ne_sm_bridge);
1015		}
1016	} else {
1017		error = 0;
1018	}
1019
1020    client_notify(serv->serviceID, serv->sid, makeref(serv), event, error, CLIENT_FLAG_NOTIFY_EVENT, ppp_getstatus(serv));
1021}
1022
1023/* -----------------------------------------------------------------------------
1024----------------------------------------------------------------------------- */
1025static
1026void socket_pppd_status(struct client *client, struct msg *msg)
1027{
1028    struct service		*serv = ppp_find(msg);
1029    void 		*data = &msg->data[MSG_DATAOFF(msg)];
1030    u_int32_t		status = *(u_int32_t *)data;
1031    u_int32_t		devstatus = *(u_int32_t *)(data + 4);
1032
1033    msg->hdr.m_len = 0xFFFFFFFF; // no reply
1034
1035    if (!serv)
1036        return;
1037
1038    ppp_updatestatus(serv, status, devstatus);
1039
1040}
1041
1042/* -----------------------------------------------------------------------------
1043----------------------------------------------------------------------------- */
1044static
1045void socket_pppd_phase(struct client *client, struct msg *msg)
1046{
1047    struct service		*serv = ppp_find(msg);
1048    void 		*data = &msg->data[MSG_DATAOFF(msg)];
1049    u_int32_t		phase = *(u_int32_t *)data;
1050    u_int32_t		ifunit = *(u_int32_t *)(data + 4);
1051
1052    msg->hdr.m_len = 0xFFFFFFFF; // no reply
1053
1054    if (!serv)
1055        return;
1056
1057    ppp_updatephase(serv, phase, ifunit);
1058
1059}
1060
1061/* -----------------------------------------------------------------------------
1062----------------------------------------------------------------------------- */
1063static
1064CFMutableDictionaryRef prepare_entity(CFMutableDictionaryRef opts, CFStringRef entity, CFStringRef property)
1065{
1066    CFMutableDictionaryRef 	dict;
1067
1068    dict = (CFMutableDictionaryRef)CFDictionaryGetValue(opts, entity);
1069    // make sure we get a valid dictionary here
1070    if (dict && (CFGetTypeID(dict) != CFDictionaryGetTypeID()))
1071        return 0;
1072
1073    if (dict == 0) {
1074    	dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1075        if (dict == 0)
1076            return 0;
1077        CFDictionaryAddValue((CFMutableDictionaryRef)opts, entity, dict);
1078        CFRelease(dict);
1079    }
1080    CFDictionaryRemoveValue(dict, property);
1081
1082    return dict;
1083}
1084
1085
1086/* -----------------------------------------------------------------------------
1087----------------------------------------------------------------------------- */
1088static
1089int set_long_opt(CFMutableDictionaryRef opts, CFStringRef entity, CFStringRef property, u_long opt, u_long mini, u_long maxi, u_long limit)
1090{
1091    CFMutableDictionaryRef 	dict;
1092    CFNumberRef 		num;
1093
1094    if (opt < mini) {
1095        if (limit) opt = mini;
1096        else return EINVAL;
1097    }
1098    else if (opt > maxi) {
1099        if (limit) opt = maxi;
1100        else return EINVAL;
1101    }
1102
1103    dict = prepare_entity(opts, entity, property);
1104    if (dict == 0)
1105        return ENOMEM;
1106
1107    num = CFNumberCreate(NULL, kCFNumberLongType, &opt);
1108    if (num) {
1109        CFDictionaryAddValue(dict, property, num);
1110        CFRelease(num);
1111    }
1112
1113    return 0;
1114}
1115
1116/* -----------------------------------------------------------------------------
1117----------------------------------------------------------------------------- */
1118static
1119int set_str_opt(CFMutableDictionaryRef opts, CFStringRef entity, CFStringRef property, char *opt, int len, CFStringRef optref)
1120{
1121    CFMutableDictionaryRef 	dict;
1122    CFStringRef 		str;
1123
1124    dict = prepare_entity(opts, entity, property);
1125    if (dict == 0)
1126        return ENOMEM;
1127
1128    if (optref)
1129        CFDictionaryAddValue(dict, property, optref);
1130    else {
1131        opt[len] = 0;
1132        str = CFStringCreateWithCString(NULL, opt, kCFStringEncodingUTF8);
1133        if (str) {
1134            CFDictionaryAddValue(dict, property, str);
1135            CFRelease(str);
1136        }
1137    }
1138    return 0;
1139}
1140
1141/* -----------------------------------------------------------------------------
1142----------------------------------------------------------------------------- */
1143static
1144int set_array_opt(CFMutableDictionaryRef opts, CFStringRef entity, CFStringRef property, CFStringRef optref1, CFStringRef optref2)
1145{
1146    CFMutableDictionaryRef 	dict;
1147    CFMutableArrayRef 		array;
1148
1149    dict = prepare_entity(opts, entity, property);
1150    if (dict == 0)
1151        return ENOMEM;
1152
1153    array = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
1154    if (array == 0)
1155        return ENOMEM;
1156
1157    if (optref1)
1158        CFArrayAppendValue(array, optref1);
1159    if (optref2)
1160        CFArrayAppendValue(array, optref2);
1161
1162    CFDictionaryAddValue(dict, property, array);
1163    CFRelease(array);
1164
1165    return 0;
1166}
1167
1168/* -----------------------------------------------------------------------------
1169----------------------------------------------------------------------------- */
1170static
1171void remove_opt(CFMutableDictionaryRef opts, CFStringRef entity, CFStringRef property)
1172{
1173    CFMutableDictionaryRef	dict;
1174
1175    dict = (CFMutableDictionaryRef)CFDictionaryGetValue(opts, entity);
1176    // make sure we get a valid dictionary here
1177    if (dict&& (CFGetTypeID(dict) == CFDictionaryGetTypeID()))
1178        CFDictionaryRemoveValue(dict, property);
1179}
1180
1181
1182/* -----------------------------------------------------------------------------
1183id must be a valid client
1184----------------------------------------------------------------------------- */
1185static
1186void socket_setoption(struct client *client, struct msg *msg, void **reply)
1187{
1188    struct ppp_opt			*opt = ALIGNED_CAST(struct ppp_opt *)&msg->data[MSG_DATAOFF(msg)];
1189    u_int32_t				optint = *ALIGNED_CAST(u_int32_t *)(&opt->o_data[0]);
1190    u_char					*optstr = &opt->o_data[0];
1191    CFMutableDictionaryRef	opts;
1192    u_int32_t				err = 0, len = msg->hdr.m_len - sizeof(struct ppp_opt_hdr), speed;
1193    struct service			*serv = ppp_find(msg);
1194    CFStringRef				string1, string2;
1195
1196    if (!serv) {
1197        msg->hdr.m_result = ENODEV;
1198        msg->hdr.m_len = 0;
1199        return;
1200    }
1201
1202	if (client->flags & CLIENT_FLAG_SWAP_BYTES) {
1203		opt->o_type = htonl(opt->o_type);
1204		optint = htonl(optint);
1205	}
1206
1207    // not connected, set the client options that will be used.
1208    opts = client_findoptset(client, serv->serviceID);
1209    if (!opts) {
1210        // first option used by client, create private set
1211        opts = client_newoptset(client, serv->serviceID);
1212        if (!opts) {
1213            msg->hdr.m_result = ENOMEM;
1214            msg->hdr.m_len = 0;
1215            return;
1216        }
1217    }
1218
1219    switch (opt->o_type) {
1220
1221        // COMM options
1222        case PPP_OPT_DEV_NAME:
1223            err = set_str_opt(opts, kSCEntNetInterface, kSCPropNetInterfaceDeviceName, (char*)optstr, len, 0);
1224            break;
1225        case PPP_OPT_DEV_SPEED:
1226            // add flexibility and adapt the speed to the immediatly higher speed
1227            speed = optint;
1228            if (speed <= 1200) speed = 1200;
1229            else if ((speed > 1200) && (speed <= 2400)) speed = 2400;
1230            else if ((speed > 2400) && (speed <= 9600)) speed = 9600;
1231            else if ((speed > 9600) && (speed <= 19200)) speed = 19200;
1232            else if ((speed > 19200) && (speed <= 38400)) speed = 38400;
1233            else if ((speed > 38400) && (speed <= 57600)) speed = 57600;
1234            else if ((speed > 38400) && (speed <= 57600)) speed = 57600;
1235            else if ((speed > 57600) && (speed <= 0xFFFFFFFF)) speed = 115200;
1236            err = set_long_opt(opts, kSCEntNetModem, kSCPropNetModemSpeed, speed, 0, 0xFFFFFFFF, 0);
1237            break;
1238        case PPP_OPT_DEV_CONNECTSCRIPT:
1239            err = set_str_opt(opts, kSCEntNetModem, kSCPropNetModemConnectionScript, (char*)optstr, len, 0);
1240            break;
1241        case PPP_OPT_DEV_DIALMODE:
1242            string1 = kSCValNetModemDialModeWaitForDialTone;
1243            switch (optint) {
1244                case PPP_DEV_IGNOREDIALTONE:
1245                    string1 = kSCValNetModemDialModeIgnoreDialTone;
1246                    break;
1247                case PPP_DEV_MANUALDIAL:
1248                    string1 = kSCValNetModemDialModeManual;
1249                    break;
1250            }
1251            if (string1)
1252                set_str_opt(opts, kSCEntNetModem, kSCPropNetModemDialMode, 0, 0, string1);
1253            break;
1254        case PPP_OPT_COMM_TERMINALMODE:
1255            switch (optint) {
1256                case PPP_COMM_TERM_NONE:
1257                    remove_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommDisplayTerminalWindow);
1258                    remove_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommUseTerminalScript);
1259                    break;
1260                case PPP_COMM_TERM_SCRIPT:
1261                    err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommUseTerminalScript, 1, 0, 1, 1)
1262                    	|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommDisplayTerminalWindow, 0, 0, 1, 1);
1263                    break;
1264                case PPP_COMM_TERM_WINDOW:
1265                    err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommUseTerminalScript, 0, 0, 1, 1)
1266                    	|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommDisplayTerminalWindow, 1, 0, 1, 1);
1267                    break;
1268            }
1269	    break;
1270        case PPP_OPT_COMM_TERMINALSCRIPT:
1271            err = set_str_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommTerminalScript, (char*)optstr, len, 0);
1272            break;
1273        case PPP_OPT_COMM_REMOTEADDR:
1274            err = set_str_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommRemoteAddress, (char*)optstr, len, 0);
1275            break;
1276        case PPP_OPT_COMM_IDLETIMER:
1277            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPDisconnectOnIdleTimer, optint, 0, 0xFFFFFFFF, 1)
1278                || set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPDisconnectOnIdle, optint, 0, 1, 1);
1279            break;
1280        case PPP_OPT_COMM_SESSIONTIMER:
1281            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPSessionTimer, optint, 0, 0xFFFFFFFF, 1)
1282            	|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPUseSessionTimer, optint, 0, 1, 1);
1283            break;
1284        case PPP_OPT_COMM_CONNECTDELAY:
1285            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPCommConnectDelay, optint, 0, 0xFFFFFFFF, 1);
1286            break;
1287
1288            // LCP options
1289        case PPP_OPT_LCP_HDRCOMP:
1290            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPCompressionPField, optint & PPP_LCP_HDRCOMP_PROTO, 0, 1, 1)
1291                || set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPCompressionACField, optint & PPP_LCP_HDRCOMP_ADDR, 0, 1, 1);
1292            break;
1293        case PPP_OPT_LCP_MRU:
1294            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPMRU, optint, 0, 0xFFFFFFFF, 1);
1295            break;
1296        case PPP_OPT_LCP_MTU:
1297            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPMTU, optint, 0, 0xFFFFFFFF, 1);
1298            break;
1299        case PPP_OPT_LCP_RCACCM:
1300            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPReceiveACCM, optint, 0, 0xFFFFFFFF, 1);
1301            break;
1302        case PPP_OPT_LCP_TXACCM:
1303            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPTransmitACCM, optint, 0, 0xFFFFFFFF, 1);
1304            break;
1305        case PPP_OPT_LCP_ECHO:
1306            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPEchoInterval, (ALIGNED_CAST(struct ppp_opt_echo *)opt->o_data)->interval, 0, 0xFFFFFFFF, 1)
1307            	|| set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPLCPEchoFailure, (ALIGNED_CAST(struct ppp_opt_echo *)opt->o_data)->failure, 0, 0xFFFFFFFF, 1);
1308            break;
1309
1310            // SEC options
1311        case PPP_OPT_AUTH_PROTO:
1312            string1 = string2 = 0;
1313            switch (optint) {
1314                case PPP_AUTH_NONE:
1315                    string1 = CFSTR("None");//kSCValNetPPPAuthProtocolNone;
1316                    break;
1317                case PPP_AUTH_PAP:
1318                    string1 = kSCValNetPPPAuthProtocolPAP;
1319                    break;
1320                case PPP_AUTH_CHAP:
1321                    string2 = kSCValNetPPPAuthProtocolCHAP;
1322                    break;
1323                case PPP_AUTH_PAPCHAP:
1324                    string1 = kSCValNetPPPAuthProtocolPAP;
1325                    string2 = kSCValNetPPPAuthProtocolCHAP;
1326                    break;
1327                default:
1328                    err = EINVAL;
1329            }
1330            if (string1 || string2)
1331                err = set_array_opt(opts, kSCEntNetPPP, kSCPropNetPPPAuthProtocol, string1, string2);
1332            break;
1333        case PPP_OPT_AUTH_NAME:
1334           err = set_str_opt(opts, kSCEntNetPPP, kSCPropNetPPPAuthName, (char*)optstr, len, 0);
1335            break;
1336        case PPP_OPT_AUTH_PASSWD:
1337            err = set_str_opt(opts, kSCEntNetPPP, kSCPropNetPPPAuthPassword, (char*)optstr, len, 0);
1338            break;
1339
1340            // IPCP options
1341        case PPP_OPT_IPCP_HDRCOMP:
1342            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPIPCPCompressionVJ, optint, 0, 1, 1);
1343            break;
1344        case PPP_OPT_IPCP_REMOTEADDR:
1345            string1 = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d.%d.%d.%d"),
1346                optint >> 24, (optint >> 16) & 0xFF, (optint >> 8) & 0xFF, optint & 0xFF);
1347            if (string1) {
1348                err = set_array_opt(opts, kSCEntNetIPv4, kSCPropNetIPv4DestAddresses, string1, 0);
1349                CFRelease(string1);
1350            }
1351            break;
1352        case PPP_OPT_IPCP_LOCALADDR:
1353            string1 = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d.%d.%d.%d"),
1354                optint >> 24, (optint >> 16) & 0xFF, (optint >> 8) & 0xFF, optint & 0xFF);
1355            if (string1) {
1356                err = set_array_opt(opts, kSCEntNetIPv4, kSCPropNetIPv4Addresses, string1, 0);
1357                CFRelease(string1);
1358            }
1359            break;
1360            // MISC options
1361        case PPP_OPT_LOGFILE:
1362            err = EOPNOTSUPP;
1363            //err = set_str_opt(&opts->misc.logfile, optstr, len);
1364            break;
1365        case PPP_OPT_COMM_REMINDERTIMER:
1366            err = set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPIdleReminderTimer, optint, 0, 0xFFFFFFFF, 1)
1367                || set_long_opt(opts, kSCEntNetPPP, kSCPropNetPPPIdleReminder, optint, 0, 1, 1);
1368            break;
1369        case PPP_OPT_ALERTENABLE:
1370            err = set_long_opt(opts, kSCEntNetPPP, CFSTR("AlertEnable"), optint, 0, 0xFFFFFFFF, 1);
1371            break;
1372        default:
1373            err = EOPNOTSUPP;
1374    };
1375
1376    msg->hdr.m_result = err;
1377    msg->hdr.m_len = 0;
1378}
1379
1380/* -----------------------------------------------------------------------------
1381----------------------------------------------------------------------------- */
1382static
1383void socket_getoption (struct client *client, struct msg *msg, void **reply)
1384{
1385    struct ppp_opt 		*opt = ALIGNED_CAST(struct ppp_opt *)&msg->data[MSG_DATAOFF(msg)];
1386    CFDictionaryRef		opts;
1387    struct service		*serv = ppp_find(msg);
1388    u_int8_t			optdata[OPT_STR_LEN + 1] __attribute__ ((aligned(4)));		// Wcast-align fix - force alignment
1389    u_int32_t			optlen;
1390
1391    if (!serv) {
1392        msg->hdr.m_len = 0;
1393        msg->hdr.m_result = ENODEV;
1394        return;
1395    }
1396
1397	if (client->flags & CLIENT_FLAG_SWAP_BYTES)
1398		opt->o_type = htonl(opt->o_type);
1399
1400    if (serv->u.ppp.phase != PPP_IDLE)
1401        // take the active user options
1402        opts = serv->connectopts;
1403    else
1404        // not connected, get the client options that will be used.
1405        opts = client_findoptset(client, serv->serviceID);
1406
1407    if (!ppp_getoptval(serv, opts, 0, opt->o_type, optdata, sizeof(optdata), &optlen)) {
1408        msg->hdr.m_len = 0;
1409        msg->hdr.m_result = EOPNOTSUPP;
1410        return;
1411    }
1412
1413	*reply = my_Allocate(sizeof(struct ppp_opt_hdr) + optlen);
1414    if (*reply == 0) {
1415        msg->hdr.m_result = ENOMEM;
1416        msg->hdr.m_len = 0;
1417        return;
1418    }
1419
1420	if (client->flags & CLIENT_FLAG_SWAP_BYTES) {
1421		switch(opt->o_type) {
1422			// all 4 bytes options
1423			case PPP_OPT_DEV_SPEED:
1424			case PPP_OPT_COMM_IDLETIMER:
1425			case PPP_OPT_AUTH_PROTO:
1426			case PPP_OPT_LCP_HDRCOMP:
1427			case PPP_OPT_LCP_MRU:
1428			case PPP_OPT_LCP_MTU:
1429			case PPP_OPT_LCP_RCACCM:
1430			case PPP_OPT_LCP_TXACCM:
1431			case PPP_OPT_IPCP_HDRCOMP:
1432			case PPP_OPT_IPCP_LOCALADDR:
1433			case PPP_OPT_IPCP_REMOTEADDR:
1434			case PPP_OPT_RESERVED:
1435			case PPP_OPT_COMM_REMINDERTIMER:
1436			case PPP_OPT_ALERTENABLE:
1437			case PPP_OPT_COMM_CONNECTDELAY:
1438			case PPP_OPT_COMM_SESSIONTIMER:
1439			case PPP_OPT_COMM_TERMINALMODE:
1440			case PPP_OPT_DEV_CONNECTSPEED:
1441			case PPP_OPT_DEV_DIALMODE:
1442			case PPP_OPT_DIALONDEMAND:
1443			case PPP_OPT_LCP_ECHO:
1444				*ALIGNED_CAST(u_int32_t*)optdata = htonl(*ALIGNED_CAST(u_int32_t*)optdata);
1445				break;
1446		}
1447	}
1448
1449    bcopy(opt, *reply, sizeof(struct ppp_opt_hdr));
1450    bcopy(optdata, (*reply) + sizeof(struct ppp_opt_hdr), optlen);
1451
1452    msg->hdr.m_result = 0;
1453    msg->hdr.m_len = sizeof(struct ppp_opt_hdr) + optlen;
1454}
1455
1456/* -----------------------------------------------------------------------------
1457----------------------------------------------------------------------------- */
1458void socket_client_notify (CFSocketRef ref, u_char *sid, u_int32_t link, u_long event, u_long error, u_int32_t flags)
1459{
1460    struct ppp_msg_hdr	msg;
1461	int link_len = 0;
1462
1463	bzero(&msg, sizeof(msg));
1464	msg.m_type = PPP_EVENT;
1465	msg.m_link = link;
1466	msg.m_result = event;
1467	msg.m_cookie = error;
1468	if (sid) {
1469		msg.m_flags |= USE_SERVICEID;
1470		msg.m_link = strlen((char*)sid);
1471		link_len = msg.m_link; /* save len */
1472	}
1473
1474	/* swap back bytes that have been assigned by internal processing functions */
1475	if (flags & CLIENT_FLAG_SWAP_BYTES) {
1476		msg.m_flags = htons(msg.m_flags);
1477		msg.m_type = htons(msg.m_type);
1478		msg.m_result = htonl(msg.m_result);
1479		msg.m_cookie = htonl(msg.m_cookie);
1480		msg.m_link = htonl(msg.m_link);
1481		msg.m_len = htonl(msg.m_len);
1482	}
1483
1484	if (writen(CFSocketGetNative(ref), &msg, sizeof(msg)) != sizeof(msg))
1485		return;
1486
1487	if (sid) {
1488		if (writen(CFSocketGetNative(ref), sid, link_len) != link_len)
1489			return;
1490	}
1491}
1492
1493