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
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <netdb.h>
29#include <sys/param.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <netinet/in.h>
33#include <arpa/inet.h>
34#include <net/route.h>
35#include <net/if_types.h>
36#include <unistd.h>
37#include <netinet/ip.h>
38#include <netinet/bootp.h>
39#include "pppd.h"
40#include "fsm.h"
41#include "pathnames.h"
42#include "acsp.h"
43#include "acscp.h"
44#include "acscp_plugin.h"
45#include <net/if.h> 		// required for if_ppp.h
46#include "../../Family/if_ppp.h"
47#include "../vpnd/RASSchemaDefinitions.h"
48#include <CoreFoundation/CoreFoundation.h>
49#include <SystemConfiguration/SystemConfiguration.h>
50#include <SystemConfiguration/SCSchemaDefinitions.h>
51
52#define ACSP_TIMEOUT_VALUE	3	/* seconds */
53
54static acsp_ext *ext_list;		// list of acsp_ext structs - one for each option type
55//static struct acsp_plugin *plugin_list;	// list of plugin channels - not currently used
56static bool acsp_plugin_installed = false; //To check ACSP status externally
57
58extern bool	acsp_no_routes;
59extern bool	acsp_no_domains;
60
61extern bool	acsp_use_dhcp;
62extern bool	acsp_intercept_dhcp;
63
64
65extern char *serverid; 	// defined in sys-MacOSX.c
66extern char *serviceid; // defined in sys-MacOSX.c
67
68
69static void acsp_start_plugin(acsp_ext *ext, int mtu);
70static void acsp_stop_plugin(acsp_ext *ext);
71static void acsp_output(acsp_ext *ext);
72static void acsp_timeout(void *arg);
73static void acsp_ipdata_input(int unit, u_char *pkt, int len, u_int32_t ouraddr, u_int32_t hisaddr);
74static void acsp_ipdata_up(int unit, u_int32_t ouraddr, u_int32_t hisaddr);
75static void acsp_ipdata_down(int unit);
76static void acsp_ipdata_timeout(void *arg);
77static int acsp_ipdata_print(u_char *, int, void (*) __P((void *, char *, ...)), void *);
78
79// ACSP plugin channel functions - will eventually be called thru acsp_channel
80static void acsp_plugin_check_options(void);
81static int acsp_plugin_get_type_count(void);
82static int acsp_plugin_init_type(acsp_ext *ext, int type_index);
83
84
85//------------------------------------------------------------
86//------------------------------------------------------------
87// ACSP Protocol Functions
88//------------------------------------------------------------
89//------------------------------------------------------------
90
91//------------------------------------------------------------
92// acsp_init_plugins
93//------------------------------------------------------------
94void
95acsp_init_plugins(void *arg, uintptr_t phase)
96{
97    acsp_ext	*ext;
98    int		i, option_count;
99
100    ext_list = 0;
101
102    remove_notifier(&phasechange, acsp_init_plugins, 0);
103
104    // load each plugin here and call start - is this the right place ???
105
106    // for each plugin
107
108    acsp_plugin_check_options();
109
110    // call get_type_count
111    option_count = acsp_plugin_get_type_count();
112
113    // for each option the plugin supports - get the ext struct initialized
114    for (i = 0; i < option_count; i++) {
115        if ((ext = (acsp_ext*)malloc(sizeof(acsp_ext))) == 0) {
116            error("acscp unable allocate plugin structures\n");
117            acscp_protent.enabled_flag = 0;
118            return;
119        }
120
121        if (acsp_plugin_init_type(ext, i)) {
122            error("error initializing acsp plugin type\n");
123            free(ext);
124            continue;
125        }
126
127        ext->next = 0;
128        ext->timer_state = ACSP_TIMERSTATE_STOPPED;
129        ext->in.size = sizeof(ACSP_Input);
130        ext->in.mtu = 0;
131        ext->in.log_debug = 0;
132        ext->in.log_error = 0;
133        ext->out.size = sizeof(ACSP_Output);
134
135        // add the ext struct to the list
136        ext->next = ext_list;
137        ext_list = ext;
138    }
139
140	// add hook for dhcp/ipdata
141	// will use route and domain information from acsp
142	ipdata_input_hook = acsp_ipdata_input;
143	ipdata_up_hook = acsp_ipdata_up;
144	ipdata_down_hook = acsp_ipdata_down;
145	ipdata_print_hook = acsp_ipdata_print;
146
147}
148
149//------------------------------------------------------------
150// acsp_start_plugin
151//------------------------------------------------------------
152void
153acsp_start(int mtu)
154{
155    acsp_ext	*ext;
156
157	// acscp is negotiated, stop dhcp
158	acsp_ipdata_down(0);
159
160    // let the plugins do their thing
161    ext = ext_list;
162    while (ext) {
163        acsp_start_plugin(ext, mtu);
164        ext = ext->next;
165    }
166
167}
168
169//------------------------------------------------------------
170// acsp_start_plugin
171//------------------------------------------------------------
172void
173acsp_stop(void)
174{
175    acsp_ext	*ext;
176
177    // stop the plugins
178    ext = ext_list;
179    while (ext) {
180        acsp_stop_plugin(ext);
181        ext = ext->next;
182    }
183}
184
185//------------------------------------------------------------
186// acsp_start_plugin
187//------------------------------------------------------------
188static void
189acsp_start_plugin(acsp_ext *ext, int mtu)
190{
191    ext->in.mtu = mtu;
192    ext->in.notification = ACSP_NOTIFICATION_START;
193    ext->in.data_len = 0;
194    ext->in.data = 0;
195    ext->out.action = ACSP_ACTION_NONE;
196    ext->out.data_len = 0;
197    ext->out.data = 0;
198    ext->process(ext->context, &ext->in, &ext->out);
199
200    acsp_output(ext);
201}
202
203//------------------------------------------------------------
204// acsp_stop_plugin
205//------------------------------------------------------------
206static void
207acsp_stop_plugin(acsp_ext *ext)
208{
209    if (ext->timer_state != ACSP_TIMERSTATE_STOPPED) {
210        UNTIMEOUT(acsp_timeout, ext);
211        ext->timer_state = ACSP_TIMERSTATE_STOPPED;
212    }
213    ext->in.notification = ACSP_NOTIFICATION_STOP;
214    ext->in.data_len = 0;
215    ext->in.data = 0;
216    ext->out.action = ACSP_ACTION_NONE;
217    ext->out.data_len = 0;
218    ext->out.data = 0;
219    ext->process(ext->context, &ext->in, &ext->out);
220}
221
222/* Wcast-align fix - The input and output buffers used by pppd are aligned(4)
223 * which makes them still aligned(4) after the 4 byte ppp header is removed.
224 * These routines expect the packet buffer to have such alignment.
225 */
226
227//------------------------------------------------------------
228// acsp_data_input
229//------------------------------------------------------------
230void
231acsp_data_input(int unit, u_char *packet, int len)
232{
233
234    acsp_ext 	*ext;
235    acsp_packet *pkt = ALIGNED_CAST(acsp_packet*)packet;
236
237    if (len < ACSP_HDR_SIZE) {
238        error("ACSP packet received was too short\n");
239        return;		// discard the packet
240    }
241
242    // find the option struct and plugin for this packet
243    for (ext = ext_list; ext != 0; ext = ext->next)
244        if (ext->type == pkt->type)
245            break;
246
247    if (ext == 0) {
248        error("ACSP packet received with invalid type\n");
249        return;
250    }
251
252    if (ntohs(pkt->flags) & ACSP_FLAG_ACK && ext->timer_state == ACSP_TIMERSTATE_PACKET && pkt->seq == ext->last_seq) {
253            UNTIMEOUT(acsp_timeout, ext);
254            ext->timer_state = ACSP_TIMERSTATE_STOPPED;
255    }
256    ext->in.notification = ACSP_NOTIFICATION_PACKET;
257    ext->in.data = pkt;
258    ext->in.data_len = len;
259    ext->out.data = 0;
260    ext->out.data_len = 0;
261    ext->out.action = ACSP_ACTION_NONE;
262
263    ext->process(ext->context, &ext->in, &ext->out);
264    acsp_output(ext);
265}
266
267//------------------------------------------------------------
268// acsp_output
269//------------------------------------------------------------
270static void
271acsp_output(acsp_ext *ext)
272{
273    int 	done = 0;
274    u_int8_t	*ptr;
275
276    while (!done) {
277        done = 1;
278        switch (ext->out.action) {
279            case ACSP_ACTION_NONE:
280                break;
281
282            case ACSP_ACTION_SEND:
283            case ACSP_ACTION_SEND_WITH_TIMEOUT:
284                if (ext->out.data_len > ext->in.mtu || ext->out.data_len < ACSP_HDR_SIZE) {
285                    error("ACSP plugin for option # %d trying to send packet of invalid length\n", ext->type);
286                    ext->in.notification = ACSP_NOTIFICATION_ERROR;
287                    ext->in.data = 0;
288                    ext->in.data_len = 0;
289                    ext->out.data = 0;
290                    ext->out.data_len = 0;
291                    ext->out.action = ACSP_ACTION_NONE;
292                    done = 0;
293                    ext->process(ext->context, &ext->in, &ext->out);
294                } else {
295                    ptr = ext->out.data;	// add address, control, and proto
296                    *ptr++ = 0xff;
297                    *ptr++ = 0x03;
298                    *(ALIGNED_CAST(u_int16_t*)ptr) = htons(PPP_ACSP);
299                    ptr += 2;
300
301                    if (ext->out.action == ACSP_ACTION_SEND_WITH_TIMEOUT) {
302                        if (ext->timer_state != ACSP_TIMERSTATE_STOPPED)
303                            UNTIMEOUT(acsp_timeout, ext);
304                        ext->timer_state = ACSP_TIMERSTATE_PACKET;
305                        ext->last_seq = (ALIGNED_CAST(acsp_packet*)ptr)->seq;
306                        TIMEOUT(acsp_timeout, ext, ACSP_TIMEOUT_VALUE);
307                    }
308                    output(0, ext->out.data, ext->out.data_len);
309                    if (ext->free)
310                        ext->free(ext->context, &ext->out);
311
312                }
313                break;
314
315            case ACSP_ACTION_INVOKE_UI:
316                error("ACSP plugin for option # %d attempted to invoke UI - not supported\n", ext->type);
317                ext->in.notification = ACSP_NOTIFICATION_ERROR;
318                ext->in.data = 0;
319                ext->in.data_len = 0;
320                ext->out.data = 0;
321                ext->out.data_len = 0;
322                ext->out.action = ACSP_ACTION_NONE;
323                done = 0;
324                ext->process(ext->context, &ext->in, &ext->out);
325                break;
326
327            case ACSP_ACTION_SET_TIMEOUT:
328                if (ext->out.data_len != 4) {
329                    error("ACSP plugin for option # %d attempted timeout action with invalid time value\n", ext->type);
330                    ext->in.notification = ACSP_NOTIFICATION_ERROR;
331                    ext->in.data = 0;
332                    ext->in.data_len = 0;
333                    ext->out.data = 0;
334                    ext->out.data_len = 0;
335                    ext->out.action = ACSP_ACTION_NONE;
336                    done = 0;
337                    ext->process(ext->context, &ext->in, &ext->out);
338                } else {
339                    ext->timer_state = ACSP_TIMERSTATE_GENERAL;
340                    TIMEOUT(acsp_timeout, ext, *((int*)ext->out.data));
341                }
342                break;
343
344            case ACSP_ACTION_CANCEL_TIMEOUT:
345                if (ext->timer_state != ACSP_TIMERSTATE_STOPPED) {
346                    UNTIMEOUT(acsp_timeout, ext);
347                    ext->timer_state = ACSP_TIMERSTATE_STOPPED;
348                }
349                break;
350
351            default:
352                error("ACSP plugin for option # %d trying to perform invalid action\n", ext->type);
353                ext->in.notification = ACSP_NOTIFICATION_ERROR;
354                ext->in.data = 0;
355                ext->in.data_len = 0;
356                ext->out.data = 0;
357                ext->out.data_len = 0;
358                ext->out.action = ACSP_ACTION_NONE;
359                done = 0;
360                ext->process(ext->context, &ext->in, &ext->out);
361                break;
362        } // switch
363    } // while
364}
365
366
367//------------------------------------------------------------
368// acsp_timeout
369//------------------------------------------------------------
370static void
371acsp_timeout(void *arg)
372{
373    acsp_ext	*ext = (acsp_ext*)arg;
374
375    ext->timer_state = ACSP_TIMERSTATE_STOPPED;
376    ext->in.notification = ACSP_NOTIFICATION_TIMEOUT;
377    ext->in.data_len = 0;
378    ext->in.data = 0;
379    ext->out.action = ACSP_ACTION_NONE;
380    ext->out.data_len = 0;
381    ext->out.data = 0;
382    ext->process(ext->context, &ext->in, &ext->out);
383
384    acsp_output(ext);
385}
386
387//------------------------------------------------------------
388//------------------------------------------------------------
389//  Plugin stuff - to be moved to plugin eventually
390//------------------------------------------------------------
391//------------------------------------------------------------
392
393#define STR_BUFSIZE			1024
394#define ACSP_PLUGIN_MAX_RETRIES		10
395
396//
397// plugin state
398//
399#define	PLUGIN_STATE_NONE		0	/* no state set */
400#define PLUGIN_STATE_INITIAL		1	/* setup but not started - ACSP not up */
401#define PLUGIN_RCVSTATE_LISTEN		2	/* listening for incomming data */
402#define PLUGIN_RCVSTATE_WAITING_PKT	3	/* waiting for more data - partial data recvd */
403#define PLUGIN_SENDSTATE_WAITING_ACK	4	/* waiting for ack for last packet sent */
404#define PLUGIN_STATE_DONE		5	/* done sending */
405
406//
407// modes to indicate sending or receiving
408//
409#define PLUGIN_MODE_NONE		0
410#define PLUGIN_MODE_RCV			1
411#define PLUGIN_MODE_SEND		2
412
413#define ACSP_ROUTEFLAGS_PRIVATE		0x0001
414#define ACSP_ROUTEFLAGS_PUBLIC		0x0002
415
416//
417// route struct to hold 1 route for sending or
418// a route received to be installed
419//
420typedef struct acsp_route {
421    struct acsp_route		*next;
422    struct in_addr		address;
423    struct in_addr		mask;
424    struct in_addr		router;
425    u_int16_t			flags;
426    int				installed;
427} acsp_route;
428
429// structure of route data in a packet
430typedef struct acsp_route_data {
431    u_int32_t		address;
432    u_int32_t		mask;
433    u_int16_t		flags;
434    u_int16_t		reserved;
435} acsp_route_data;
436
437//
438// domain struct to hold 1 domain name for sending or
439// a domain name recieved to be installed
440//
441typedef struct acsp_domain {
442    struct acsp_domain		*next;
443    struct in_addr		server;
444    char			*name;
445} acsp_domain;
446
447// structure of domain data in a packet
448#define ACSP_DOMAIN_DATA_HDRSIZE    6
449typedef struct acsp_domain_data {
450    u_int32_t	server;
451    u_int16_t	len;
452    char	name[1];
453} acsp_domain_data;
454
455//
456// plugin context - holds all state for a particular
457// option type
458//
459typedef struct acsp_plugin_context {
460    u_int8_t		type;
461    int			mode;		// indicates client or server side
462    int			state;
463    u_int8_t		next_seq;
464    int			ip_up;
465    // the following is used only by the server side
466    int			retry_count;
467    void		*next_to_send;
468    char		*buf;
469    u_int16_t		buf_size;
470    u_int16_t		last_pkt_len;
471    void		*list;
472    int			config_installed;
473} acsp_plugin_context;
474
475typedef struct acsp_dhcp_context {
476    int				state;
477    int				retry_count;
478    void			*route;
479    char			*domain;
480    struct in_addr	netmask;
481    int				config_installed;
482	int				unit;
483    struct in_addr	ouraddr;
484    struct in_addr	hisaddr;
485} acsp_dhcp_context;
486
487//
488// globals
489//
490
491// lists of routes and domains read from the config file
492// these lists are placed in the context for the accociated
493// option type when the context is created and intialized
494static acsp_route 	*routes_list = 0;
495static acsp_domain 	*domains_list = 0;
496static struct in_addr	primary_router = { 0 };		// address of primary router (before PPP connection)
497static u_int32_t	subnet_mask = 0;
498
499static acsp_dhcp_context *dhcp_context = 0;
500
501extern CFStringRef 		serviceidRef;
502extern SCDynamicStoreRef	cfgCache;
503extern int publish_dns_wins_entry(CFStringRef entity, CFStringRef property1, CFTypeRef ref1, CFTypeRef ref1a,
504						CFStringRef property2, CFTypeRef ref2,
505						CFStringRef property3, CFTypeRef ref3, int clean);
506extern int route_interface(int cmd, struct in_addr host, struct in_addr mask, char iftype, char *ifname, int is_host);
507extern int route_gateway(int cmd, struct in_addr dest, struct in_addr mask, struct in_addr gateway, int use_gway_flag);
508
509//
510// funtion prototypes
511//
512static void acsp_plugin_ip_up(void *arg, uintptr_t phase);
513static void acsp_plugin_ip_down(void *arg, uintptr_t phase);
514static void acsp_plugin_install_config(acsp_plugin_context *context);
515static void acsp_plugin_remove_config(acsp_plugin_context *context);
516static void acsp_plugin_read_routes(CFPropertyListRef serverRef);
517static void acsp_plugin_read_domains(CFPropertyListRef serverRef);
518static void acsp_plugin_read_domain_from_store(void);
519static int acsp_plugin_alloc_context(acsp_ext *ext, u_int8_t type);
520static void acsp_plugin_send(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out);
521static void acsp_plugin_resend(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out);
522static void acsp_plugin_send_ack(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out);
523static int acsp_plugin_read(acsp_plugin_context* context, ACSP_Input* acsp_in);
524static void acsp_plugin_clear_list(acsp_plugin_context* context);
525static void acsp_plugin_dispose __P((void *context));
526static void acsp_plugin_process __P((void *context, ACSP_Input *acsp_in, ACSP_Output *acsp_out));
527static void acsp_plugin_print_packet __P((void (*printer)(void *, char *, ...), void *arg, u_char code, char *inbuf, int insize));
528
529static void acsp_plugin_add_routes(acsp_route *route);
530static void acsp_plugin_add_domains(acsp_domain	*domain);
531static void acsp_plugin_remove_routes(acsp_route *route);
532
533//------------------------------------------------------------
534// *** Plugin functions called thru Plugin Channel ***
535//	Used to init the plugin and init the ext structs
536//	for each option type supported
537//------------------------------------------------------------
538
539//------------------------------------------------------------
540// acsp_plugin_check_options
541//	read options from the config file
542//------------------------------------------------------------
543static void acsp_plugin_check_options(void)
544{
545
546    SCPreferencesRef 	prefs;
547    CFPropertyListRef	ref;
548    CFStringRef		idRef;
549    char			sopt[32];
550    CFTypeRef		dictRef;
551    CFStringRef		key;
552    CFDataRef		strRef;
553    CFPropertyListRef	servers_list;
554
555
556    routes_list = 0;
557    domains_list = 0;
558
559    if (serverid == 0) {	// client side
560        if (acsp_no_routes == 0)			// command line disable ?
561            acscp_wantoptions[0].neg_routes = 1;	// default setting for client
562        if (acsp_no_domains == 0) 			// command line disable ?
563            acscp_wantoptions[0].neg_domains = 1;	// default setting for client
564
565        // get the primary interface router address
566        sopt[0] = 0;
567        key = SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetIPv4);
568        if (key) {
569            if ((dictRef = SCDynamicStoreCopyValue(cfgCache, key))) {
570                strRef = CFDictionaryGetValue(dictRef, kSCPropNetIPv4Router);
571                if (strRef && (CFGetTypeID(strRef) == CFStringGetTypeID())) {
572                    CFStringGetCString((CFStringRef)strRef, sopt, 32, kCFStringEncodingUTF8);
573                }
574                CFRelease(dictRef);
575            }
576            CFRelease(key);
577        }
578        if (sopt[0])
579            inet_aton(sopt, &primary_router);
580        else
581            primary_router.s_addr = 0;
582    } else {
583        /* open the prefs file */
584        if ((prefs = SCPreferencesCreate(0, CFSTR("pppd"), kRASServerPrefsFileName)) != 0) {
585            // get servers list from the plist
586            if ((servers_list = SCPreferencesGetValue(prefs, kRASServers)) != 0) {
587                if ((idRef = CFStringCreateWithCString(0, serverid , kCFStringEncodingMacRoman)) != 0) {
588                    if ((ref = CFDictionaryGetValue(servers_list, idRef)) != 0) {
589                        if (acsp_no_routes == 0)
590                            acsp_plugin_read_routes(ref);	// get routes option configuration
591                        if (acsp_no_domains == 0)
592                            acsp_plugin_read_domains(ref);	// get domains option configuration
593                    }
594                    CFRelease(idRef);
595                }
596                CFRelease(prefs);
597            }
598        }
599    }
600}
601
602//------------------------------------------------------------
603// acsp_plugin_read_routes
604//	read routes option configuration
605//------------------------------------------------------------
606static void acsp_plugin_read_routes(CFPropertyListRef serverRef)
607{
608
609    CFDictionaryRef		dict;
610    CFStringRef			stringRef;
611    CFArrayRef			addrArrayRef, maskArrayRef, typeArrayRef;
612    int				addrCount, maskCount, typeCount, i;
613    char			addrStr[STR_BUFSIZE], maskStr[STR_BUFSIZE];
614    acsp_route			*route;
615    struct in_addr 		outAddr, outMask;
616    u_int32_t			flags;
617
618    // routes option parameters
619    dict = CFDictionaryGetValue(serverRef, kRASEntIPv4);
620    if (dict && CFGetTypeID(dict) == CFDictionaryGetTypeID()) {
621        // get the route address array
622        addrArrayRef  = CFDictionaryGetValue(dict, kRASPropIPv4OfferedRouteAddresses);
623        if (addrArrayRef && CFGetTypeID(addrArrayRef) == CFArrayGetTypeID()) {
624            addrCount = CFArrayGetCount(addrArrayRef);
625            // get subnet mask array
626            maskArrayRef  = CFDictionaryGetValue(dict, kRASPropIPv4OfferedRouteMasks);
627            if (maskArrayRef && CFGetTypeID(maskArrayRef) == CFArrayGetTypeID()) {
628                maskCount = CFArrayGetCount(addrArrayRef);
629                // get route type array
630                typeArrayRef  = CFDictionaryGetValue(dict, kRASPropIPv4OfferedRouteTypes);
631                if (typeArrayRef && CFGetTypeID(typeArrayRef) == CFArrayGetTypeID()) {
632                    typeCount = CFArrayGetCount(typeArrayRef);
633
634                    if (addrCount != maskCount || addrCount != typeCount) {
635                        error("ACSP plugin: while reading prefs - route address, mask, and type counts not equal\n");
636                        return;
637                    }
638                    acscp_allowoptions[0].neg_routes = 1;	// found routes - set negotiate flag
639
640                    // get address and mask for each route
641                    for (i = 0; i < addrCount; i++) {
642                        stringRef = CFArrayGetValueAtIndex(addrArrayRef, i);
643                        addrStr[0] = 0;
644                        CFStringGetCString(stringRef, addrStr, STR_BUFSIZE, kCFStringEncodingUTF8);
645                        // get mask
646                        stringRef = CFArrayGetValueAtIndex(maskArrayRef, i);
647                        maskStr[0] = 0;
648                        CFStringGetCString(stringRef, maskStr, STR_BUFSIZE, kCFStringEncodingUTF8);
649                        // get route type
650                        stringRef = CFArrayGetValueAtIndex(typeArrayRef, i);
651                        if (CFStringCompare(stringRef, kRASValIPv4OfferedRouteTypesPrivate, 0) == kCFCompareEqualTo)
652                            flags = ACSP_ROUTEFLAGS_PRIVATE;
653                        else if(CFStringCompare(stringRef, kRASValIPv4OfferedRouteTypesPublic, 0) == kCFCompareEqualTo)
654                            flags = ACSP_ROUTEFLAGS_PUBLIC;
655                        else {
656                            error("ACSP plugin: invalid route type specified\n");
657                            acscp_allowoptions[0].neg_routes = 0;
658                            break;
659                        }
660                        // allocate the structure
661                        if ((route = (acsp_route*)malloc(sizeof(acsp_route))) == 0) {
662                            error("ACSP plugin: no memory\n");
663                            acscp_allowoptions[0].neg_routes = 0;
664                            break;
665                        }
666						bzero(route, sizeof(acsp_route));
667                        // convert
668                        if (inet_aton(addrStr, &outAddr) == 0 || inet_aton(maskStr, &outMask) == 0) {
669                            error("ACSP plugin: invalid ip address or mask specified\n");
670                            free(route);
671                            acscp_allowoptions[0].neg_routes = 0;
672                            return;
673                        }
674                        route->address = outAddr;
675                        route->mask = outMask;
676                        route->flags = flags;
677                        route->installed = 0;
678
679                        route->next = routes_list;
680                        routes_list = route;
681                    }
682                    // check if we really have something to send
683                    if (routes_list == 0)
684                        acscp_allowoptions[0].neg_routes = 0;
685                }
686            }
687        }
688    }
689}
690
691//------------------------------------------------------------
692// acsp_plugin_read_domains
693//	read the domains option configuration
694//------------------------------------------------------------
695static void acsp_plugin_read_domains(CFPropertyListRef serverRef)
696{
697    CFDictionaryRef		dict;
698    CFStringRef			stringRef;
699    CFArrayRef			nameArrayRef, serverArrayRef;
700    int				nameCount, serverCount, outlen, i;
701    char			str[STR_BUFSIZE];
702    acsp_domain			*domain;
703    struct in_addr		outAddr;
704
705    // domains option parameters
706    dict = CFDictionaryGetValue(serverRef, kRASEntDNS);
707    if (dict && CFGetTypeID(dict) == CFDictionaryGetTypeID()) {
708        // get the domain names aray
709        nameArrayRef  = CFDictionaryGetValue(dict, kRASPropDNSOfferedSearchDomains);
710        if (nameArrayRef && CFGetTypeID(nameArrayRef) == CFArrayGetTypeID()) {
711            nameCount = CFArrayGetCount(nameArrayRef);
712            // get the search domain server addresses if present
713            serverArrayRef  = CFDictionaryGetValue(dict, kRASPropDNSOfferedSearchDomainServers);
714            if (serverArrayRef && CFGetTypeID(serverArrayRef) == CFArrayGetTypeID()) {
715                serverCount = CFArrayGetCount(serverArrayRef);
716                if (serverArrayRef && (serverCount != nameCount)) {
717                    error("ACSP plugin: search domain count not equal to search domain server count\n");
718                    return;
719                }
720            } else
721                serverCount = 0;
722
723            acscp_allowoptions[0].neg_domains = 1;	// enable negotiation of this option
724            if (nameCount == 0)
725               acsp_plugin_read_domain_from_store();
726            else {
727                for (i = 0; i < nameCount; i++) {
728                    stringRef = CFArrayGetValueAtIndex(nameArrayRef, i);
729                    str[0] = 0;
730                    CFStringGetCString(stringRef, str, STR_BUFSIZE, kCFStringEncodingUTF8);
731                    outlen = strlen(str);
732                    if (outlen) {
733                        if ((domain = (acsp_domain*)malloc(sizeof(acsp_domain))) == 0) {
734                            error("ACSP plugin: no memory\n");
735                            acscp_allowoptions[0].neg_domains = 0;
736                            break;
737                        }
738                        if ((domain->name = malloc(outlen + 1)) == 0) {
739                            error("ACSP plugin: no memory\n");
740                            acscp_allowoptions[0].neg_domains = 0;
741                            free(domain);
742                            break;
743                        }
744                        memcpy(domain->name, str, outlen + 1);
745                        if (serverCount) {
746                            stringRef = CFArrayGetValueAtIndex(serverArrayRef, i);
747                            str[0] = 0;
748                            CFStringGetCString(stringRef, str, STR_BUFSIZE, kCFStringEncodingUTF8);
749                            if (inet_aton(str, &outAddr) == 0) {
750                                error("ACSP plugin: invalid ip address specified for DNS server\n");
751                                free(domain);
752                                acscp_allowoptions[0].neg_domains = 0;
753                                return;
754                            }
755                            domain->server = outAddr;
756                        } else
757                            domain->server.s_addr = 0;
758                        domain->next = domains_list;
759                        domains_list = domain;
760                    }
761                }
762            }
763            // check if we really have something to send
764            if (domains_list == 0)
765                acscp_allowoptions[0].neg_domains = 0;
766        }
767    }
768}
769
770//------------------------------------------------------------
771// acsp_plugin_get_type_count
772//	returns the number of types supported by the plugin
773//------------------------------------------------------------
774static void acsp_plugin_read_domain_from_store(void)
775{
776    SCDynamicStoreRef 	storeRef;
777    CFPropertyListRef	ref = 0;
778    CFStringRef		key = 0;
779    CFStringRef		stringRef;
780    char 		str[STR_BUFSIZE];
781    int			len;
782    acsp_domain		*domain;
783
784    storeRef = SCDynamicStoreCreate(0, CFSTR("pppd"), 0, 0);
785    if (storeRef) {
786        key = SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetDNS);
787        if (key) {
788            ref = SCDynamicStoreCopyValue(storeRef, key);
789            if (ref && CFGetTypeID(ref) == CFDictionaryGetTypeID()) {
790                stringRef = CFDictionaryGetValue(ref, kSCPropNetDNSDomainName);
791                if (stringRef && CFGetTypeID(stringRef) == CFStringGetTypeID()) {
792                    str[0] = 0;
793                    CFStringGetCString(stringRef, str, STR_BUFSIZE, kCFStringEncodingUTF8);
794                    len = strlen(str);
795                    if (len) {
796                        if ((domain = (acsp_domain*)malloc(sizeof(acsp_domain))) == 0) {
797                            error("ACSP plugin: no memory\n");
798                        } else if ((domain->name = malloc(len + 1)) == 0) {
799                            error("ACSP plugin: no memory\n");
800                            free(domain);
801                        } else {
802                            memcpy(domain->name, str, len + 1);
803                            domain->server.s_addr = 0;
804                            domain->next = domains_list;
805                            domains_list = domain;
806                        }
807                    }
808                }
809            }
810            CFRelease(key);
811            if (ref)
812                CFRelease(ref);
813        }
814        CFRelease(storeRef);
815    }
816}
817
818//------------------------------------------------------------
819// acsp_plugin_get_type_count
820//	returns the number of types supported by the plugin
821//------------------------------------------------------------
822static int acsp_plugin_get_type_count(void)
823{
824    return 2;
825}
826
827//------------------------------------------------------------
828// acsp_plugin_init_type
829//	init the ext struct for an option type
830//	the option being inited is determined by the index
831//	the function get_type_count() is used to get the
832//	number of option types the plugin supports
833//------------------------------------------------------------
834static int acsp_plugin_init_type(acsp_ext *ext, int type_index)
835{
836    if (type_index == 0) {
837        ext->type = CI_ROUTES;
838        if (acsp_plugin_alloc_context(ext, CI_ROUTES) != 0)
839            return -1;
840        ((acsp_plugin_context*)(ext->context))->list = routes_list;	// save the list read from prefs into the context
841    } else if (type_index == 1) {
842        ext->type = CI_DOMAINS;
843        if (acsp_plugin_alloc_context(ext, CI_DOMAINS) != 0)
844            return -1;
845        ((acsp_plugin_context*)(ext->context))->list = domains_list;	// save the list read from prefs into the context
846    } else
847        return -1;	// bad index
848
849    // we want the routes notifier to fire first, then the domains
850    if (ext->type == CI_DOMAINS)
851        add_notifier_last(&ip_up_notify, acsp_plugin_ip_up, ext->context);
852    else
853        add_notifier(&ip_up_notify, acsp_plugin_ip_up, ext->context);
854    add_notifier(&ip_down_notify, acsp_plugin_ip_down, ext->context);
855
856    return 0;
857}
858
859//------------------------------------------------------------
860// acsp_plugin_alloc_context
861//	allocate a context for the specified type
862//------------------------------------------------------------
863static int acsp_plugin_alloc_context(acsp_ext *ext, u_int8_t type)
864{
865    acsp_plugin_context* context;
866
867    if ((context = (acsp_plugin_context*)malloc(sizeof(acsp_plugin_context))) == 0) {
868        error("ACSP plugin: no memory\n");
869        return -1;
870    }
871    // allocte buffer using max mtu since nothing has been negotiated yet
872    if ((context->buf = malloc(PPP_MTU)) == 0) {
873        error("ACSP plugin: no memory\n");
874        free(context);
875        return -1;
876    }
877    context->buf_size = PPP_MTU;
878    context->type = type;
879    context->mode = PLUGIN_MODE_NONE;
880    context->state = PLUGIN_STATE_INITIAL;
881    context->ip_up = 0;
882    context->retry_count = 0;
883    context->next_seq = 0;
884    context->last_pkt_len = 0;
885    context->list = 0;
886    context->config_installed = 0;
887    ext->context = context;
888
889    // setup funtions pointers
890    ext->dispose = acsp_plugin_dispose;
891    ext->free = 0;
892    ext->interactive_ui = 0;
893    ext->process = acsp_plugin_process;
894    ext->print_packet = acsp_plugin_print_packet;
895
896    return 0;
897}
898
899//------------------------------------------------------------
900// *** Plugin functions called thru the ext struct ***
901//	Called for a particular option type
902//------------------------------------------------------------
903
904//------------------------------------------------------------
905// acsp_plugin_dispose
906//	clean up
907//------------------------------------------------------------
908static void acsp_plugin_dispose(void *context)
909{
910    acsp_plugin_clear_list((acsp_plugin_context*)context);
911}
912
913//------------------------------------------------------------
914// acsp_plugin_process
915//	handle an event
916//------------------------------------------------------------
917static void acsp_plugin_process(void *context, ACSP_Input *acsp_in, ACSP_Output *acsp_out)
918{
919    acsp_packet			*pkt = (acsp_packet*)acsp_in->data;
920    acsp_plugin_context*	theContext = (acsp_plugin_context*)context;
921
922    switch (acsp_in->notification) {
923        case ACSP_NOTIFICATION_NONE:
924            break;
925
926        case ACSP_NOTIFICATION_DATA_FROM_UI:
927            error("ACSP plugin: unexpected notification received\n");
928            break;
929
930        case ACSP_NOTIFICATION_START:
931            // get negotiated values and setup modes in context
932            if (theContext->type == CI_ROUTES) {
933                if (acscp_gotoptions[0].neg_routes)
934                    theContext->mode = PLUGIN_MODE_RCV;
935                else if (acscp_hisoptions[0].neg_routes)
936                    theContext->mode = PLUGIN_MODE_SEND;
937                else
938                    theContext->mode = PLUGIN_MODE_NONE;
939            } else {
940                if (acscp_gotoptions[0].neg_domains)
941                    theContext->mode = PLUGIN_MODE_RCV;
942                else if (acscp_hisoptions[0].neg_domains)
943                    theContext->mode = PLUGIN_MODE_SEND;
944                else
945                    theContext->mode = PLUGIN_MODE_NONE;
946            }
947            // setup the context and send if required
948            theContext->next_seq = 0;
949            theContext->retry_count = 0;
950
951            if (theContext->mode == PLUGIN_MODE_SEND) {
952                acsp_plugin_send(theContext, acsp_in, acsp_out);
953                theContext->next_seq++;
954                theContext->state = PLUGIN_SENDSTATE_WAITING_ACK;
955            }
956            else if (theContext->mode == PLUGIN_MODE_RCV)
957                theContext->state = PLUGIN_RCVSTATE_LISTEN;
958            break;
959
960        case ACSP_NOTIFICATION_STOP:
961            switch (theContext->state) {
962            	case PLUGIN_SENDSTATE_WAITING_ACK:
963                    acsp_out->action = ACSP_ACTION_CANCEL_TIMEOUT;
964                    // fall thru
965
966                case PLUGIN_STATE_DONE:
967                    if (theContext->config_installed) {
968                        theContext->config_installed = 0;
969                        if (theContext->type == CI_ROUTES)
970                            acsp_plugin_remove_config(theContext);
971                    }
972                    break;
973            }
974            // if client side - clear the list of received data
975            if (theContext->mode == PLUGIN_MODE_RCV)
976                acsp_plugin_clear_list(theContext);
977            theContext->state = PLUGIN_STATE_INITIAL;
978            break;
979
980        case ACSP_NOTIFICATION_PACKET:
981            switch (theContext->state) {
982                case PLUGIN_RCVSTATE_LISTEN:
983                    if ((ntohs(pkt->flags) & ACSP_FLAG_START) == 0) {
984                        error("ACSP plugin: received first packet with no start flag\n");
985                        break;
986                    } else
987                        theContext->state = PLUGIN_RCVSTATE_WAITING_PKT;
988                    // fall thru
989
990                case PLUGIN_RCVSTATE_WAITING_PKT:
991                    // copy route data
992                    if (pkt->seq == theContext->next_seq) {
993                        if (acsp_plugin_read(theContext, acsp_in) == 0) {
994                            theContext->next_seq++;
995                            if (ntohs(pkt->flags) & ACSP_FLAG_END) {
996                                theContext->state = PLUGIN_STATE_DONE;
997                                if (theContext->ip_up)
998                                    acsp_plugin_install_config(theContext);
999                                else
1000                                    notify(acspdhcpready_notifier, 0);
1001                            }
1002                        } else
1003                            break;					// bad packet or error - send no ack
1004                    }
1005
1006                    if (ntohs(pkt->flags) & ACSP_FLAG_REQUIRE_ACK)
1007                        acsp_plugin_send_ack(theContext, acsp_in, acsp_out);
1008                    break;
1009
1010                case PLUGIN_SENDSTATE_WAITING_ACK:
1011                    if (ntohs(pkt->flags) & ACSP_FLAG_ACK) {	// if ack - process it - otherwise drop the packet
1012                        if (theContext->next_to_send) {
1013                            acsp_plugin_send(theContext, acsp_in, acsp_out);
1014                            theContext->next_seq++;
1015                        }
1016                        else {
1017                            theContext->state = PLUGIN_STATE_DONE;
1018                            notify(acspdhcpready_notifier, 0);
1019                        }
1020                    }
1021                    break;
1022            }
1023            break;
1024
1025        case ACSP_NOTIFICATION_TIMEOUT:
1026            if (theContext->state == PLUGIN_SENDSTATE_WAITING_ACK) {
1027                if (theContext->retry_count++ < ACSP_PLUGIN_MAX_RETRIES)
1028                    acsp_plugin_resend(theContext, acsp_in, acsp_out);
1029                else {
1030                    error("ACSP plugin: no acknowlegement from peer\n");
1031                    theContext->state = PLUGIN_STATE_DONE;
1032                    notify(acspdhcpready_notifier, 0);
1033                }
1034            } else
1035                error("ACSP plugin: received unexpected timeout\n");
1036            break;
1037
1038        case ACSP_NOTIFICATION_ERROR:
1039            error("ACSP plugin: error notificationr received\n");
1040            if (theContext->state == PLUGIN_SENDSTATE_WAITING_ACK)
1041                acsp_out->action = ACSP_ACTION_CANCEL_TIMEOUT;
1042            theContext->state = PLUGIN_STATE_DONE;
1043            notify(acspdhcpready_notifier, 0);
1044            break;
1045
1046        default:
1047            break;
1048    }
1049}
1050
1051//------------------------------------------------------------
1052// acsp_plugin_print_packet
1053//------------------------------------------------------------
1054static void acsp_plugin_print_packet(void (*printer)(void *, char *, ...), void *arg, u_char code, char *inbuf, int insize)
1055{
1056}
1057
1058//------------------------------------------------------------
1059// acsp_plugin_send
1060//	send a data packet
1061//------------------------------------------------------------
1062static void acsp_plugin_send(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out)
1063{
1064
1065    acsp_packet 	*pkt;
1066    u_int16_t		space, len;
1067    int			slen;
1068    acsp_route_data	*route_data;
1069    acsp_domain_data	domain_data;
1070    u_int8_t *outPtr;
1071
1072
1073    pkt = ALIGNED_CAST(acsp_packet*)(context->buf + 4);	// leave space for address, control, and protocol
1074    space = MIN(context->buf_size, acsp_in->mtu) - 4;
1075
1076    if (context->state == PLUGIN_STATE_INITIAL) {
1077        pkt->flags = htons(ACSP_FLAG_START);
1078        context->next_to_send = context->list;
1079    } else
1080        pkt->flags = 0;
1081
1082    pkt->type = context->type;
1083    pkt->seq = context->next_seq;
1084    pkt->flags = htons(ntohs(pkt->flags) | ACSP_FLAG_REQUIRE_ACK);
1085    pkt->reserved = 0;
1086    len = ACSP_HDR_SIZE;
1087
1088    switch (context->type) {
1089        case CI_ROUTES:
1090            route_data = ALIGNED_CAST(acsp_route_data*)pkt->data;
1091            while (context->next_to_send && space >= len + sizeof(acsp_route_data)) {
1092                route_data->address = ((acsp_route*)context->next_to_send)->address.s_addr;
1093                route_data->mask = ((acsp_route*)context->next_to_send)->mask.s_addr;
1094                route_data->flags = htons(((acsp_route*)context->next_to_send)->flags);
1095                route_data->reserved = htons(0);
1096                len += sizeof(acsp_route_data);
1097                context->next_to_send = ((acsp_route*)(context->next_to_send))->next;
1098                route_data++;
1099            }
1100            break;
1101    	case CI_DOMAINS:
1102            // WCast-align fix - use memcpy for unaligned access
1103            outPtr = pkt->data;
1104            while (context->next_to_send && space >= (len + (slen = strlen(((acsp_domain*)(context->next_to_send))->name)) + 6)) {
1105                domain_data.server = ((acsp_domain*)(context->next_to_send))->server.s_addr;
1106                domain_data.len = htons(slen);
1107                memcpy(outPtr, &domain_data, ACSP_DOMAIN_DATA_HDRSIZE);
1108                memcpy(outPtr + ACSP_DOMAIN_DATA_HDRSIZE, ((acsp_domain*)(context->next_to_send))->name, slen);
1109                len += (slen + ACSP_DOMAIN_DATA_HDRSIZE);
1110                context->next_to_send = ((acsp_domain*)(context->next_to_send))->next;
1111                outPtr += (ACSP_DOMAIN_DATA_HDRSIZE + slen);
1112            }
1113            break;
1114    }
1115
1116    if (context->next_to_send == 0)
1117		pkt->flags = htons(ntohs(pkt->flags) | ACSP_FLAG_END);
1118    acsp_out->action = ACSP_ACTION_SEND_WITH_TIMEOUT;
1119    context->last_pkt_len = len;
1120    pkt->len = htons(len);
1121    acsp_out->data_len = len + 4;
1122    acsp_out->data = context->buf;
1123}
1124
1125//------------------------------------------------------------
1126// acsp_plugin_resend
1127//	re-send a data packet
1128//------------------------------------------------------------
1129static void acsp_plugin_resend(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out)
1130{
1131    // resent the packet;
1132    acsp_out->data = context->buf;
1133    acsp_out->data_len = context->last_pkt_len + 4;
1134    acsp_out->action = ACSP_ACTION_SEND_WITH_TIMEOUT;
1135}
1136
1137//------------------------------------------------------------
1138// acsp_plugin_send_ack
1139//	send an ack
1140//------------------------------------------------------------
1141static void acsp_plugin_send_ack(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out)
1142{
1143    acsp_packet 	*inPkt, *outPkt;
1144
1145    inPkt = (acsp_packet*)acsp_in->data;
1146    outPkt = ALIGNED_CAST(acsp_packet*)(context->buf + 4);	// leave space for address, control, and protocol
1147    outPkt->type = context->type;
1148    outPkt->seq = inPkt->seq;
1149    outPkt->flags = htons(ACSP_FLAG_ACK);
1150    outPkt->len = htons(ACSP_HDR_SIZE);
1151    outPkt->reserved = 0;
1152
1153    acsp_out->action = ACSP_ACTION_SEND;
1154    acsp_out->data_len = ACSP_HDR_SIZE + 4;
1155    acsp_out->data = context->buf;
1156}
1157
1158//------------------------------------------------------------
1159// acsp_plugin_read
1160//	process an incomming data packet
1161//------------------------------------------------------------
1162static int acsp_plugin_read(acsp_plugin_context* context, ACSP_Input* acsp_in)
1163{
1164    acsp_packet 	*pkt;
1165    acsp_route		*route;
1166    acsp_domain		*domain;
1167    int			len, domain_len;
1168    acsp_route_data	*route_data;
1169    acsp_domain_data domain_data;
1170    u_int8_t        *inPtr;
1171
1172    len = acsp_in->data_len - ACSP_HDR_SIZE;
1173    pkt = acsp_in->data;
1174
1175    switch (context->type) {
1176        case CI_ROUTES:
1177            if (len % 4 != 0) {
1178                error("ACSP plugin: received packet with invalid len\n");
1179                return -1;
1180            }
1181            route_data = ALIGNED_CAST(acsp_route_data*)pkt->data;
1182            while (len > 4) {
1183                if ((route = (acsp_route*)malloc(sizeof(acsp_route))) == 0) {
1184                    error("ACSP plugin: no memory\n");
1185                    return -1;
1186                }
1187				bzero(route, sizeof(acsp_route));
1188                route->address.s_addr = route_data->address & route_data->mask;
1189                route->mask.s_addr = route_data->mask;
1190                route->flags = ntohs(route_data->flags);
1191                route->installed = 0;
1192                route->next = (acsp_route*)context->list;
1193                context->list = route;
1194                len -= sizeof(acsp_route_data);
1195                route_data++;
1196            }
1197            break;
1198
1199        case CI_DOMAINS:
1200            // WCast-align fix - this routine changed to use memcpy for unaligned access
1201            inPtr = pkt->data;
1202            while (len > 2) {
1203                memcpy(&domain_data, inPtr, ACSP_DOMAIN_DATA_HDRSIZE);
1204                if ((domain = (acsp_domain*)malloc(sizeof(acsp_domain))) == 0) {
1205                    error("ACSP plugin: no memory\n");
1206                    return -1;
1207                }
1208                domain_len = ntohs(domain_data.len);
1209                if ((domain->name = malloc(domain_len + 1)) == 0) {
1210                    error("ACSP plugin: no memory\n");
1211                    free(domain);
1212                    return -1;
1213                }
1214                domain->server.s_addr = domain_data.server;
1215                memcpy(domain->name, inPtr + ACSP_DOMAIN_DATA_HDRSIZE, domain_len);
1216                *(domain->name + domain_len) = 0;	// zero terminate
1217                domain->next = (acsp_domain*)context->list;
1218                context->list = domain;
1219                len -= (domain_len + ACSP_DOMAIN_DATA_HDRSIZE);
1220                inPtr += (ACSP_DOMAIN_DATA_HDRSIZE + domain_len);
1221            }
1222            break;
1223
1224        default:
1225            return -1;
1226            break;
1227    }
1228    return 0;
1229}
1230
1231//------------------------------------------------------------
1232// acsp_plugin_clear_list
1233//	clear the route or domain list in the context
1234//------------------------------------------------------------
1235static void acsp_plugin_clear_list(acsp_plugin_context* context)
1236{
1237    void* temp = context->list;
1238
1239    while (temp) {
1240        if (context->type == CI_ROUTES)
1241            context->list = ((acsp_route*)temp)->next;
1242        else {
1243            free(((acsp_domain*)temp)->name);
1244            context->list = ((acsp_domain*)temp)->next;
1245        }
1246        free(temp);
1247        temp = context->list;
1248    }
1249}
1250
1251//------------------------------------------------------------
1252// acsp_ip_up
1253//	called when ipcp comes up
1254//------------------------------------------------------------
1255static void acsp_plugin_ip_up(void *arg, uintptr_t phase)
1256{
1257    acsp_plugin_context* context = (acsp_plugin_context*)arg;
1258
1259    context->ip_up = 1;
1260    if (context->mode == PLUGIN_MODE_RCV
1261            && context->state == PLUGIN_STATE_DONE
1262            && context->config_installed == 0)
1263        acsp_plugin_install_config(context);
1264    else
1265        notify(acspdhcpready_notifier, 0);
1266}
1267
1268//------------------------------------------------------------
1269// acsp_ip_down
1270//	called when ipcp goes down
1271//------------------------------------------------------------
1272static void acsp_plugin_ip_down(void *arg, uintptr_t phase)
1273{
1274    acsp_plugin_context* context = (acsp_plugin_context*)arg;
1275
1276    context->ip_up = 0;
1277    if (context->mode == PLUGIN_MODE_RCV && context->config_installed)
1278        acsp_plugin_remove_config(context);
1279
1280}
1281
1282//------------------------------------------------------------
1283// acsp_install_config
1284//	called do add the config for the option
1285//	specified by the context
1286//------------------------------------------------------------
1287static void acsp_plugin_install_config(acsp_plugin_context *context)
1288{
1289    if (context->type == CI_ROUTES)
1290        acsp_plugin_add_routes(context->list);
1291    else
1292        acsp_plugin_add_domains(context->list);
1293    context->config_installed = 1;
1294    acsp_plugin_installed = true;
1295
1296    /* Signal that acsp info is ready */
1297    notify(acspdhcpready_notifier, 0);
1298}
1299
1300//------------------------------------------------------------
1301// acsp_remove_config
1302//	called do remove the config for the option
1303//	specified by the context
1304//------------------------------------------------------------
1305static void acsp_plugin_remove_config(acsp_plugin_context *context)
1306{
1307    // we don't remove anything - pppd will cleanup the dynamic store
1308    if (context->type == CI_ROUTES)
1309        acsp_plugin_remove_routes(context->list);
1310    acsp_plugin_installed = false;
1311    context->config_installed = 0;
1312}
1313
1314//------------------------------------------------------------
1315// acsp_plugin_add_routes
1316//	install routes
1317//------------------------------------------------------------
1318static void acsp_plugin_add_routes(acsp_route *route)
1319{
1320	char   route_str[INET_ADDRSTRLEN];
1321	char   mask_str[INET_ADDRSTRLEN];
1322	char   gateway_str[INET_ADDRSTRLEN];
1323    struct in_addr	addr;
1324	int err;
1325
1326    if (route) {	// only if routes are present
1327        sleep(1);
1328        addr.s_addr = 0;
1329
1330        // remove current default route
1331		cifdefaultroute(0, 0, 0);
1332		cifroute();
1333#if 0
1334        if (route_gateway(RTM_DELETE, addr, addr, addr, 1) == 0)
1335            error("ACSP plugin: error removing default route\n");
1336        // add new default route on previous primary interface
1337        if (route_gateway(RTM_ADD, addr, addr, primary_router, 1) == 0) {
1338            error("ACSP plugin: error adding default route\n");
1339            return;
1340        }
1341#endif
1342
1343        while (route) {
1344            route->installed = 1;
1345            if (route->flags & ACSP_ROUTEFLAGS_PRIVATE) {
1346				if (route->address.s_addr == 0)
1347					sifdefaultroute(0, 0, 0);
1348				else {
1349					//if (route->router.s_addr)
1350					//	err = route_gateway(RTM_ADD, route->address, route->mask, route->router, 1);
1351					//else
1352						err = route_interface(RTM_ADD, route->address, route->mask, IFT_PPP, ifname, 0);
1353					if (err == 0) {
1354						error("ACSP plugin: error installing private net route. (%s/%s).",
1355							  addr2ascii(AF_INET, &route->address, sizeof(route->address), route_str),
1356							  addr2ascii(AF_INET, &route->mask, sizeof(route->mask), mask_str));
1357						route->installed = 0;
1358					}
1359				}
1360            } else if (route->flags & ACSP_ROUTEFLAGS_PUBLIC) {
1361				if (route->address.s_addr == 0)
1362					cifdefaultroute(0, 0, 0);
1363				else {
1364					if (route_gateway(RTM_ADD, route->address, route->mask, primary_router, 1) == 0) {
1365						error("ACSP plugin: error installing public net route. (%s/%s -> %s).",
1366							  addr2ascii(AF_INET, &route->address, sizeof(route->address), route_str),
1367							  addr2ascii(AF_INET, &route->mask, sizeof(route->mask), mask_str),
1368							  addr2ascii(AF_INET, &primary_router, sizeof(primary_router), gateway_str));
1369						route->installed = 0;
1370					}
1371				}
1372            }
1373            route = route->next;
1374        }
1375    }
1376}
1377
1378//------------------------------------------------------------
1379// acsp_plugin_remove_routes
1380//	remove routes
1381//------------------------------------------------------------
1382static void acsp_plugin_remove_routes(acsp_route *route)
1383{
1384	char   route_str[INET_ADDRSTRLEN];
1385	char   mask_str[INET_ADDRSTRLEN];
1386	char   gateway_str[INET_ADDRSTRLEN];
1387
1388    while (route) {
1389        if (route->installed) {
1390			route->installed = 0;
1391			if (route->flags & ACSP_ROUTEFLAGS_PRIVATE) {
1392				if (route->address.s_addr == 0)
1393					cifdefaultroute(0, 0, 0);
1394				else {
1395					if (route_interface(RTM_DELETE, route->address, route->mask, IFT_PPP, ifname, 0) == 0) {
1396						error("ACSP plugin: error removing private net route. (%s/%s).",
1397							  addr2ascii(AF_INET, &route->address, sizeof(route->address), route_str),
1398							  addr2ascii(AF_INET, &route->mask, sizeof(route->mask), mask_str));
1399						route->installed = 1;
1400					}
1401				}
1402			} else if (route->flags & ACSP_ROUTEFLAGS_PUBLIC) {
1403				if (route->address.s_addr == 0) {
1404					// nothing to do
1405				}
1406				else {
1407					if (route_gateway(RTM_DELETE, route->address, route->mask, primary_router, 0) == 0) {
1408						error("ACSP plugin: error removing public net route. (%s/%s -> %s).",
1409							  addr2ascii(AF_INET, &route->address, sizeof(route->address), route_str),
1410							  addr2ascii(AF_INET, &route->mask, sizeof(route->mask), mask_str),
1411							  addr2ascii(AF_INET, &primary_router, sizeof(primary_router), gateway_str));
1412						route->installed = 1;
1413					}
1414				}
1415			}
1416		}
1417        route = route->next;
1418    }
1419}
1420
1421//------------------------------------------------------------
1422// acsp_plugin_add_domains
1423//	install search domains
1424//------------------------------------------------------------
1425static void acsp_plugin_add_domains(acsp_domain	*domain)
1426{
1427    CFStringRef	str;
1428    int 	err, clean = 1;
1429	long	order = 100000;
1430	CFNumberRef num;
1431
1432	num = CFNumberCreate(NULL, kCFNumberLongType, &order);
1433	if (num == 0) {
1434		error("ACSP plugin: error adding domain name - could not create CFNumber\n");
1435		return;
1436	}
1437
1438    while (domain) {
1439        if ((str = CFStringCreateWithCString(NULL, domain->name, kCFStringEncodingUTF8))) {
1440			err = publish_dns_wins_entry(kSCEntNetDNS, kSCPropNetDNSSearchDomains, str, 0, kSCPropNetDNSSupplementalMatchDomains, str, kSCPropNetDNSSupplementalMatchOrders, num, clean);
1441#ifndef kSCPropNetProxiesSupplementalMatchDomains
1442#define kSCPropNetProxiesSupplementalMatchDomains kSCPropNetDNSSupplementalMatchDomains
1443#define kSCPropNetProxiesSupplementalMatchOrders kSCPropNetDNSSupplementalMatchOrders
1444#endif
1445			if (err) publish_dns_wins_entry(kSCEntNetProxies, kSCPropNetProxiesSupplementalMatchDomains, str, 0, kSCPropNetProxiesSupplementalMatchOrders, num, 0, 0, clean);
1446			CFRelease(str);
1447            if (err == 0) {
1448                error("ACSP plugin: error adding domain name\n");
1449				goto end;
1450            }
1451        } else {
1452            error("ACSP plugin: error adding domain name - could not create CFString\n");
1453			goto end;
1454        }
1455        domain = domain->next;
1456        clean = 0;
1457    }
1458
1459end:
1460	CFRelease(num);
1461}
1462
1463#pragma -
1464
1465/*
1466 code to act as a DHCP client and server
1467*/
1468
1469struct pseudo_udphdr {
1470	struct in_addr	src_addr;		/* source address */
1471	struct in_addr	dst_addr;		/* source address */
1472	u_int8_t		zero;			/* just zero */
1473	u_int8_t		proto;			/* destination port */
1474	u_int16_t		len;			/* packet len */
1475};
1476
1477struct dhcp {
1478    u_char              dp_op;          /* packet opcode type */
1479    u_char              dp_htype;       /* hardware addr type */
1480    u_char              dp_hlen;        /* hardware addr length */
1481    u_char              dp_hops;        /* gateway hops */
1482    u_int32_t           dp_xid;         /* transaction ID */
1483    u_int16_t           dp_secs;        /* seconds since boot began */
1484    u_int16_t           dp_flags;       /* flags */
1485    struct in_addr      dp_ciaddr;      /* client IP address */
1486    struct in_addr      dp_yiaddr;      /* 'your' IP address */
1487    struct in_addr      dp_siaddr;      /* server IP address */
1488    struct in_addr      dp_giaddr;      /* gateway IP address */
1489    u_char              dp_chaddr[16];  /* client hardware address */
1490    u_char              dp_sname[64];   /* server host name */
1491    u_char              dp_file[128];   /* boot file name */
1492    u_char              dp_options[0];  /* variable-length options field */
1493};
1494
1495struct dhcp_packet {
1496    struct ip           ip;
1497    struct udphdr       udp;
1498    struct dhcp         dhcp;
1499};
1500#define DHCP_COOKIE		0x63825363
1501
1502#define DHCP_OPTION_END				255
1503
1504#define DHCP_OPTION_LEASE_TIME			51
1505#define DHCP_OPTION_MSG_TYPE			53
1506#define DHCP_OPTION_HOST_NAME			12
1507#define DHCP_OPTION_SERVER_ID			54
1508#define DHCP_OPTION_PARAM_REQUEST_LIST	55
1509#define DHCP_OPTION_VENDOR_CLASS_ID		60
1510#define DHCP_OPTION_CLIENT_ID			61
1511
1512#define DHCP_OPTION_MSG_TYPE_ACK		5
1513#define DHCP_OPTION_MSG_TYPE_INFORM		8
1514
1515#define DHCP_OPTION_SUBNET_MASK			1
1516#define DHCP_OPTION_DNS					6
1517#define DHCP_OPTION_DOMAIN_NAME			15
1518#define DHCP_OPTION_WINS				43
1519#define DHCP_OPTION_NETBIOS				44
1520#define DHCP_OPTION_STATIC_ROUTE		249
1521
1522#define DHCP_TIMEOUT_VALUE		3	/* seconds */
1523#define DHCP_MAX_RETRIES		4
1524
1525//------------------------------------------------------------
1526// cksum
1527//------------------------------------------------------------
1528static unsigned short
1529cksum(unsigned char *data, int len)
1530{
1531	long sum = 0;
1532
1533	while (len > 1) {
1534		sum += *(ALIGNED_CAST(unsigned short *)data);
1535		data += sizeof(unsigned short);
1536		if (sum & 0x80000000)
1537			sum = (sum & 0xFFFF) + (sum >> 16);
1538		len -= 2;
1539	}
1540	if (len)
1541		sum += (unsigned short)*data;
1542	while (sum >> 16)
1543		sum = (sum & 0xFFFF) + (sum >> 16);
1544
1545	return ~sum;
1546}
1547
1548//------------------------------------------------------------
1549// log_dhcp
1550//------------------------------------------------------------
1551static void
1552log_dhcp(u_char *pkt, int len, char *text)
1553{
1554#if 0
1555	u_char *p;
1556	int i;
1557	u_int32_t cookie;
1558	struct dhcp *dp;
1559	struct dhcp_packet *packet;
1560	u_int32_t masklen, addrlen, addr, mask;
1561	char str[2048];
1562	char str2[16];
1563
1564	/* log only if debug level is super verbose */
1565	if (debug <= 1)
1566		return;
1567
1568	packet = (struct dhcp_packet *)pkt;
1569	dp = (struct dhcp *)&packet->dhcp;
1570
1571	dbglog("%s\n", text);
1572	dbglog(" op = %s\n", dp->dp_op == BOOTREQUEST ? "BOOTREQUEST" : dp->dp_op == BOOTREPLY ? "BOOTREPLY" : "UNKNOWN");
1573	dbglog(" htype = %d\n", dp->dp_htype);
1574	dbglog(" hlen = %d\n", dp->dp_hlen);
1575	dbglog(" hops = %d\n", dp->dp_hops);
1576	dbglog(" xid = %d\n", dp->dp_xid);
1577	dbglog(" flags = %d\n", dp->dp_flags);
1578	dbglog(" client address = %s\n", inet_ntoa(dp->dp_ciaddr));
1579	dbglog(" your address = %s\n", inet_ntoa(dp->dp_yiaddr));
1580	dbglog(" server address = %s\n", inet_ntoa(dp->dp_siaddr));
1581	dbglog(" gateway address = %s\n", inet_ntoa(dp->dp_giaddr));
1582
1583	dbglog(" hardware address = %X:%X:%X:%X:%X:%X\n", dp->dp_chaddr[0], dp->dp_chaddr[1], dp->dp_chaddr[2], dp->dp_chaddr[3], dp->dp_chaddr[4], dp->dp_chaddr[5]);
1584	dbglog(" hardware address = %X:%X:%X:%X:%X:%X\n", dp->dp_chaddr[6], dp->dp_chaddr[7], dp->dp_chaddr[8], dp->dp_chaddr[9], dp->dp_chaddr[10], dp->dp_chaddr[11]);
1585	dbglog(" hardware address = %X:%X:%X:%X\n", dp->dp_chaddr[12], dp->dp_chaddr[13], dp->dp_chaddr[14], dp->dp_chaddr[15]);
1586	dbglog(" server host name = %s\n", dp->dp_sname);
1587	dbglog(" boot file name = %s\n", dp->dp_file);
1588
1589	p = dp->dp_options;
1590	cookie = ntohl(*(u_int32_t *)p);
1591	if (ntohl(*(u_int32_t *)p) != DHCP_COOKIE) {
1592		dbglog(" >>> incorrect cookie = %d.%d.%d.%d\n", cookie >> 24, cookie >> 16 & 0xFF, cookie >> 8 & 0xFF,cookie & 0xFF);
1593		return;
1594	}
1595	p+=4;
1596
1597	if (*p++ != DHCP_OPTION_MSG_TYPE || *p++ != 1 || (*p != DHCP_OPTION_MSG_TYPE_INFORM && *p != DHCP_OPTION_MSG_TYPE_ACK)) {
1598		dbglog(" >>> incorrect message type = %d\n", *p);
1599		return;
1600	}
1601	dbglog(" dhcp option msg type = %s\n", *p == DHCP_OPTION_MSG_TYPE_INFORM ? "INFORM" : "ACK");
1602	p++;
1603
1604	len -= sizeof(struct dhcp_packet) + 7;
1605	while (*p != DHCP_OPTION_END && len > 0) {
1606		u_int8_t optcode, optlen;
1607
1608		optcode = *p++;
1609		// check for pad option
1610		if (optcode == 0) {
1611			len--;
1612			continue;
1613		}
1614
1615		optlen = *p++;
1616		len-=2;
1617		if (len == 0) {
1618			warning(">>> incorrect message option\n");
1619			return;
1620		}
1621
1622		str[0] = 0;
1623		switch (optcode) {
1624			case DHCP_OPTION_HOST_NAME:
1625				memcpy(str, p, optlen);
1626				str[optlen] = 0;
1627				dbglog(" dhcp option host name = %s\n", str);
1628				break;
1629			case DHCP_OPTION_VENDOR_CLASS_ID:
1630				memcpy(str, p, optlen);
1631				str[optlen] = 0;
1632				dbglog(" dhcp option vendor class id = %s\n", str);
1633				break;
1634			case DHCP_OPTION_CLIENT_ID:
1635				for (i = 0; i < optlen; i++) {
1636					snprintf(str2, sizeof(str2), "0x%x ", p[i]);
1637					strlcat(str, str2, sizeof(str));
1638				}
1639				dbglog(" dhcp option client id = %s\n", str);
1640				break;
1641			case DHCP_OPTION_SERVER_ID:
1642				for (i = 0; i < optlen; i++) {
1643					snprintf(str2, sizeof(str2), "0x%x ", p[i]);
1644					strlcat(str, str2, sizeof(str));
1645				}
1646				dbglog(" dhcp option server id = %s\n", str);
1647				break;
1648			case DHCP_OPTION_LEASE_TIME:
1649				dbglog(" dhcp option lease time = %d\n", ntohl(*(u_int32_t*)p));
1650				break;
1651			case DHCP_OPTION_SUBNET_MASK:
1652				dbglog(" dhcp option subnet mask = %d.%d.%d.%d\n", p[0], p[1], p[2], p[3]);
1653				break;
1654			case DHCP_OPTION_DOMAIN_NAME:
1655				memcpy(str, p, optlen);
1656				str[optlen] = 0;
1657				dbglog(" dhcp option domain name = %s\n", str);
1658				break;
1659			case DHCP_OPTION_STATIC_ROUTE:
1660				dbglog(" dhcp option parameter static routes = \n");
1661				i = 0;
1662				while (i < optlen) {
1663					masklen = p[i];
1664					mask = 0xFFFFFFFF << (32 - masklen);
1665					addrlen = (masklen / 8);
1666					if (masklen % 8)
1667						addrlen++;
1668					addr = ntohl(*(u_int32_t*)(&p[i+1])) & mask;
1669					router = ntohl(*(u_int32_t*)(&p[i+1+addrlen]));
1670					i += addrlen + 1 + sizeof(in_addr_t);
1671					dbglog("    route %d.%d.%d.%d mask %d.%d.%d.%d router %d.%d.%d.%d\n",
1672						(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF,
1673						(mask >> 24) & 0xFF, (mask >> 16) & 0xFF, (mask >> 8) & 0xFF, mask & 0xFF,
1674						(router >> 24) & 0xFF, (router >> 16) & 0xFF, (router >> 8) & 0xFF, router & 0xFF);
1675				}
1676/*
1677				for (i = 0; i < optlen; i++) {
1678					// UNSAFE! Don't use sprintf.
1679					sprintf(str+strlen(str), "0x%x ", p[i]);
1680				}
1681				dbglog(" dhcp option parameter static routes = %s \n", str);
1682*/
1683				break;
1684			case DHCP_OPTION_PARAM_REQUEST_LIST:
1685				for (i = 0; i < optlen; i++) {
1686					snprintf(str2, sizeof(str2), "0x%x ", p[i]);
1687					strlcat(str, str2, sizeof(str));
1688				}
1689				dbglog(" dhcp option parameter request list = %s \n", str);
1690				break;
1691
1692			default:
1693				dbglog(" dhcp option code = %d, len = %d\n", optcode, optlen);
1694				break;
1695		}
1696
1697		p+=optlen;
1698		len-=optlen;
1699	}
1700	dbglog(" end of options\n");
1701#endif
1702}
1703
1704//------------------------------------------------------------
1705// acsp_ipdata_send_packet
1706//------------------------------------------------------------
1707static void
1708acsp_ipdata_send_packet(int unit, u_char *data, int len, u_int32_t srcaddr, u_int16_t srcport, u_int32_t dstaddr, u_int16_t dstport, char *text)
1709{
1710	u_char *outp;
1711	u_int16_t checksum;
1712static u_int16_t dhcp_ip_id = 1;
1713
1714	// build a udp pseudo header for checksum calculation
1715	outp = data + PPP_HDRLEN + sizeof(struct ip) - sizeof(struct pseudo_udphdr);
1716	PUTLONG(srcaddr, outp);					// source address
1717	PUTLONG(dstaddr, outp);		// destination address
1718	PUTCHAR(0, outp);						// zero
1719	PUTCHAR(IPPROTO_UDP, outp);				// protocol
1720	PUTSHORT(len - PPP_HDRLEN - sizeof(struct ip), outp);	// total length (udp data + udp header)
1721
1722	// build udp header
1723	outp = data + PPP_HDRLEN + sizeof(struct ip);
1724	PUTSHORT(srcport, outp);			// source port
1725	PUTSHORT(dstport, outp);			// dest port
1726	PUTSHORT(len - PPP_HDRLEN - sizeof(struct ip), outp);	// total length (udp data + udp header)
1727	PUTSHORT(0, outp);						// cksum
1728	checksum = cksum(data + PPP_HDRLEN + sizeof(struct ip) - sizeof(struct pseudo_udphdr),
1729						len - PPP_HDRLEN - sizeof(struct ip) + sizeof(struct pseudo_udphdr));
1730	if (checksum == 0)
1731		checksum =0xffff;
1732	outp -= 2;								// back to cksum
1733	PUTSHORT(0, outp);						// cksum
1734
1735	// build ip header
1736	outp = data + PPP_HDRLEN;
1737	PUTSHORT(0x4500, outp);					// hdr len and service type
1738	PUTSHORT(len - PPP_HDRLEN, outp);	// total length
1739	PUTSHORT(dhcp_ip_id++, outp);			// identification
1740	PUTSHORT(0, outp);						// flags and fragment offset
1741	PUTCHAR(0x40, outp);					// ttl
1742	PUTCHAR(IPPROTO_UDP, outp);				// protocol
1743	PUTSHORT(0, outp);						// cksum
1744	PUTLONG(srcaddr, outp);					// source address
1745	PUTLONG(dstaddr, outp);		// destination address
1746	checksum = cksum(data + PPP_HDRLEN, sizeof(struct ip));
1747	outp -= 10;								// back to cksum
1748	PUTSHORT(ntohs(checksum), outp);				// header checksum
1749
1750	// log the packet
1751	log_dhcp(data + PPP_HDRLEN, len - PPP_HDRLEN, text);
1752
1753	// now it's time to send it...
1754	output(unit, data, len);
1755}
1756
1757//------------------------------------------------------------
1758// acsp_ipdata_input_server
1759//------------------------------------------------------------
1760static void
1761acsp_ipdata_input_server(int unit, u_char *pkt, int len, u_int32_t ouraddr, u_int32_t hisaddr)
1762{
1763	struct dhcp_packet *dp;
1764	struct	in_addr src;
1765	u_char *p, *outp;
1766	char str[2048];
1767	int i, outlen, pad;
1768	int need_subnet_mask = 0, need_domain_name = 0, need_static_route = 0;
1769	struct dhcp reply;
1770	u_int32_t	l;
1771
1772	dp = ALIGNED_CAST(struct dhcp_packet *)pkt;
1773
1774	/* basic length sanity check */
1775	if (len < (sizeof(struct dhcp_packet) + 7)) {  // dhcp packet + cookie + inform
1776		warning("DHCP packet received with incorrect length\n");
1777		return;
1778	}
1779
1780	log_dhcp(pkt, len, "DHCP packet received");
1781
1782	src.s_addr = dp->dhcp.dp_ciaddr.s_addr;
1783
1784	p = dp->dhcp.dp_options;
1785    GETLONG(l, p);
1786	if (l != DHCP_COOKIE) {
1787		warning("DHCP packet received with incorrect cookie\n");
1788		return;
1789	}
1790
1791	if (*p++ != DHCP_OPTION_MSG_TYPE || *p++ != 1 || *p++ != DHCP_OPTION_MSG_TYPE_INFORM) {
1792		warning("DHCP packet received with incorrect message type\n");
1793		return;
1794	}
1795
1796	len -= sizeof(struct dhcp_packet) + 7;
1797	while (*p != DHCP_OPTION_END && len > 0) {
1798		u_int8_t optcode, optlen;
1799
1800		optcode = *p++;
1801		// check for pad option
1802		if (optcode == 0) {
1803			len--;
1804			continue;
1805		}
1806
1807		optlen = *p++;
1808		len-=2;
1809		if (optlen >= len) {
1810			warning("DHCP packet received with incorrect message option\n");
1811			return;
1812		}
1813
1814		str[0] = 0;
1815		switch (optcode) {
1816			case DHCP_OPTION_PARAM_REQUEST_LIST:
1817				for (i = 0; i < optlen; i++) {
1818					if ((p[i] == DHCP_OPTION_SUBNET_MASK) && subnet_mask)
1819						need_subnet_mask = 1;
1820					else if ((p[i] == DHCP_OPTION_DOMAIN_NAME) && domains_list)
1821						need_domain_name = 1;
1822					else if ((p[i] == DHCP_OPTION_STATIC_ROUTE) && routes_list)
1823						need_static_route = 1;
1824				}
1825				break;
1826
1827			default:
1828				break;
1829		}
1830
1831		p+=optlen;
1832		len-=optlen;
1833	}
1834
1835	/* build reply dhcp packet */
1836	if (need_subnet_mask || need_domain_name || need_static_route) {
1837
1838		outp = outpacket_buf;
1839
1840		// ppp
1841		MAKEHEADER(outp, PPP_IP);
1842		outlen = PPP_HDRLEN;
1843
1844		// ip
1845		bzero(outp, sizeof(struct ip));
1846		outp += sizeof(struct ip);
1847		outlen += sizeof(struct ip);
1848
1849		// udp
1850		bzero(outp, sizeof(struct udphdr));
1851		outp += sizeof(struct udphdr);
1852		outlen += sizeof(struct udphdr);
1853
1854		// bootp
1855		memcpy(&reply, &dp->dhcp, sizeof(struct dhcp));
1856		reply.dp_op = BOOTREPLY;
1857		reply.dp_htype = 1;
1858		reply.dp_secs = 0;
1859		memcpy(outp, &reply, sizeof(struct dhcp));
1860		outlen += sizeof(struct dhcp);
1861		outp +=  sizeof(struct dhcp);
1862
1863		// dhcp options
1864		PUTLONG(DHCP_COOKIE, outp);	// dhcp cookie
1865		outlen += 4;
1866
1867		PUTCHAR(DHCP_OPTION_MSG_TYPE, outp);	// dhcp message type
1868		PUTCHAR(1, outp);		// dhcp message type len
1869		PUTCHAR(DHCP_OPTION_MSG_TYPE_ACK, outp);	// dhcp message type ack
1870		outlen += 3;
1871
1872		PUTCHAR(DHCP_OPTION_SERVER_ID, outp);	// dhcp server id
1873		PUTCHAR(4, outp);		// dhcp server id len
1874		l = ntohl(ouraddr);
1875		PUTLONG(l, outp);	// server id is our source address
1876		outlen += 6;
1877
1878		if (need_subnet_mask) {
1879			PUTCHAR(DHCP_OPTION_SUBNET_MASK, outp);	// dhcp subnet mask
1880			PUTCHAR(4, outp);		// dhcp subnet mask len
1881			PUTLONG(subnet_mask, outp);	// server mask
1882			outlen += 6;
1883		}
1884
1885		if (need_domain_name) {
1886			int len;
1887			acsp_domain 	*list = domains_list;
1888			// domain are reversed in the list use last one.
1889			while (list->next) list = list->next;
1890
1891			PUTCHAR(DHCP_OPTION_DOMAIN_NAME, outp);	// dhcp domain name
1892			len = strlen(list->name);
1893			if (outlen + len + 2 >= sizeof(outpacket_buf)) {
1894				warning("Domain name too large for DHCP\n");
1895				return;
1896			}
1897			PUTCHAR(len, outp);						// domain name len
1898			memcpy(outp, list->name, len);	// the domain
1899			outp += len;
1900			outlen += len + 2;
1901		}
1902
1903		if (need_static_route) {
1904			acsp_route 	*list = routes_list;
1905			u_int32_t mask, addr, masklen, addrlen, totlen = 0;
1906			int opdone = 0;
1907
1908			while (list) {
1909				if (list->flags & ACSP_ROUTEFLAGS_PRIVATE) {
1910					if (!opdone) {
1911						if (outlen + 2 >= sizeof(outpacket_buf)) {
1912							warning("No space for DHCP routes\n");
1913							return;
1914						}
1915						PUTCHAR(DHCP_OPTION_STATIC_ROUTE, outp);	// dhcp static route
1916						PUTCHAR(0, outp);	// dhcp static route total len
1917						opdone = 1;
1918					}
1919					mask = ntohl(list->mask.s_addr);
1920					addr = ntohl(list->address.s_addr) & mask;
1921
1922					for ( masklen = 32; masklen && (mask& 1) == 0; mask = mask >> 1, masklen--);
1923					addrlen = (masklen / 8);
1924					if (masklen % 8)
1925						addrlen++;
1926
1927					if (outlen + addrlen + 1 + 4 >= sizeof(outpacket_buf)) {
1928						warning("Static routes list too large DHCP\n");
1929						return;
1930					}
1931					PUTCHAR(masklen, outp);		// route mask
1932					PUTLONG(addr, outp);	// route address
1933					outp -= 4 - addrlen;	// move pointer back according to addr len.
1934					l = ntohl(hisaddr);
1935					PUTLONG(l, outp);	// router address
1936					totlen += addrlen + 1 + 4;
1937				}
1938				list = list->next;
1939			}
1940			if (opdone) {
1941				outp -= totlen + 1;
1942				PUTCHAR(totlen, outp);	// move back to update option len
1943				outp += totlen;
1944				outlen += totlen + 2;
1945			}
1946		}
1947
1948		PUTCHAR(DHCP_OPTION_END, outp);		// end of options
1949		outlen ++;
1950
1951		pad = outlen%4;
1952		for (i = 0; i < pad && outlen < sizeof(outpacket_buf); i++) {
1953			PUTCHAR(0, outp);		// byte padding
1954			outlen ++;
1955		}
1956
1957		// build ip/udp header and send it...
1958		acsp_ipdata_send_packet(unit, outpacket_buf, outlen, ntohl(ouraddr), IPPORT_BOOTPS, ntohl(src.s_addr), IPPORT_BOOTPC, "DHCP packet replied");
1959	}
1960}
1961
1962//------------------------------------------------------------
1963// acsp_ipdata_input_client
1964//------------------------------------------------------------
1965static void
1966acsp_ipdata_input_client(int unit, u_char *pkt, int len, u_int32_t ouraddr, u_int32_t hisaddr)
1967{
1968	struct dhcp_packet *dp;
1969	struct	in_addr src;
1970	u_char *p;
1971	u_int32_t masklen, addrlen, i, mask, l;
1972	char str[2048];
1973	acsp_route  *route;
1974	acsp_domain *domain_list = NULL, *domain;
1975	char *str_p, *tok, *delim;
1976
1977	dp = ALIGNED_CAST(struct dhcp_packet *)pkt;
1978
1979	/* basic length sanity check */
1980	if (len < (sizeof(struct dhcp_packet) + 7)) {  // dhcp packet + cookie + inform
1981		warning("DHCP packet received with incorrect length\n");
1982		return;
1983	}
1984
1985	log_dhcp(pkt, len, "DHCP packet received");
1986
1987	if (!dhcp_context) {
1988		// we didn't start DHCP or already closed it
1989		return;
1990	}
1991
1992	src.s_addr = ntohl(dp->dhcp.dp_ciaddr.s_addr);
1993
1994	p = dp->dhcp.dp_options;
1995    GETLONG(l, p);
1996	if (l != DHCP_COOKIE) {
1997		warning("DHCP packet received with incorrect cookie\n");
1998		return;
1999	}
2000
2001	if (*p++ != DHCP_OPTION_MSG_TYPE || *p++ != 1 || *p++ != DHCP_OPTION_MSG_TYPE_ACK) {
2002		warning("DHCP packet received with incorrect message type\n");
2003		return;
2004	}
2005
2006	len -= sizeof(struct dhcp_packet) + 7;
2007	while (*p != DHCP_OPTION_END && len > 0) {
2008		u_int8_t optcode, optlen;
2009
2010		optcode = *p++;
2011		// check for pad option
2012		if (optcode == 0) {
2013			len--;
2014			continue;
2015		}
2016
2017		optlen = *p++;
2018		len-=2;
2019		if (optlen >= len) {
2020			warning("DHCP packet received with incorrect message option\n");
2021			return;
2022		}
2023
2024		switch (optcode) {
2025			case DHCP_OPTION_SUBNET_MASK:
2026                memcpy(&mask, p, sizeof(u_int32_t));        // Wcast-align fix - memcpy for unaligned access
2027				if (mask &&
2028					dhcp_context->ouraddr.s_addr == ouraddr &&
2029					dhcp_context->netmask.s_addr != mask) {
2030					dhcp_context->netmask.s_addr = mask;
2031					if (!uifaddr(unit, dhcp_context->ouraddr.s_addr, dhcp_context->hisaddr.s_addr, dhcp_context->netmask.s_addr)) {
2032						notice("failed to configure dhcp option 'subnet mask' = %d.%d.%d.%d, our %x, his %x\n", p[0], p[1], p[2], p[3], ntohl(dhcp_context->ouraddr.s_addr), ntohl(dhcp_context->hisaddr.s_addr));
2033					}
2034				} else {
2035					info("ignoring dhcp option 'subnet mask' = %d.%d.%d.%d, current addr %x, current mask %x\n", p[0], p[1], p[2], p[3], ntohl(dhcp_context->ouraddr.s_addr), ntohl(dhcp_context->netmask.s_addr));
2036				}
2037				break;
2038			case DHCP_OPTION_DOMAIN_NAME:
2039
2040				if (domain_list) {
2041                    notice("ignoring dhcp option 'domain name', option already processed.\n");
2042                    break;
2043				}
2044
2045				memcpy(str, p, optlen);
2046				str[optlen] = 0;
2047				str_p = str;
2048				// check if domain is tokenized by a variety of delimiters
2049				GET_SPLITDNS_DELIM(str, delim);
2050				tok = strsep(&str_p, delim);
2051				do {
2052					if (!tok || *tok != '\0') {
2053						// tok may be NULL the first time through here.
2054						if((domain = (__typeof__(domain))malloc(sizeof(*domain))) == NULL) {
2055							error("failed to allocate domain from DHCP packet\n");
2056							break;
2057						}
2058						bzero(domain, sizeof(*domain));
2059						domain->next = domain_list;
2060						domain_list = domain;
2061						if (!tok) {
2062							domain->name = str;
2063							break;
2064						} else {
2065							domain->name = tok;
2066						}
2067					}
2068					tok = strsep(&str_p, delim);
2069				} while (tok != NULL);
2070				break;
2071			case DHCP_OPTION_STATIC_ROUTE:
2072				i = 0;
2073				while (i < optlen) {
2074					if ((route = (acsp_route*)malloc(sizeof(acsp_route))) == 0) {
2075						error("DHCP: no memory\n");
2076						return;
2077					}
2078
2079					bzero(route, sizeof(acsp_route));
2080					masklen = p[i];
2081					route->mask.s_addr = htonl(0xFFFFFFFF << (32 - masklen));
2082					addrlen = (masklen / 8);
2083					if (masklen % 8)
2084						addrlen++;
2085
2086                    memcpy(&route->address.s_addr, &p[i+1], sizeof(route->address.s_addr));         // Wcast-align fix - memcpy for unaligned access
2087					route->address.s_addr &= route->mask.s_addr;
2088					memcpy(&route->router.s_addr, &p[i+1+addrlen], sizeof(route->router.s_addr));
2089					route->flags = ACSP_ROUTEFLAGS_PRIVATE;
2090					route->installed = 0;
2091					route->next = (acsp_route*)dhcp_context->route;
2092					dhcp_context->route = route;
2093					i += addrlen + 1 + sizeof(in_addr_t);
2094				}
2095                acsp_plugin_add_routes(dhcp_context->route);
2096				break;
2097
2098			default:
2099				break;
2100		}
2101
2102		p+=optlen;
2103		len-=optlen;
2104	}
2105
2106    if (domain_list) {
2107       acsp_plugin_add_domains(domain_list);
2108        while (domain_list) {
2109            domain = domain_list;
2110            domain_list = domain_list->next;
2111            free(domain);
2112        }
2113    }
2114
2115	/* dhcp is done */
2116	UNTIMEOUT(acsp_ipdata_timeout, dhcp_context);
2117	dhcp_context->state = PLUGIN_STATE_DONE;
2118
2119    /* Signal that dhcp info is ready */
2120    notify(acspdhcpready_notifier, 0);
2121}
2122
2123//------------------------------------------------------------
2124// acsp_ipdata_start_dhcp_client
2125//------------------------------------------------------------
2126static void
2127acsp_ipdata_start_dhcp_client(int unit, u_int32_t ouraddr, u_int32_t hisaddr)
2128{
2129	u_char *outp;
2130	int i, outlen, pad;
2131static u_int16_t dhcp_ip_client_xid = 1;
2132	u_int32_t l, clientid = 1; // ??
2133
2134	outp = outpacket_buf;
2135
2136	// ppp
2137	MAKEHEADER(outp, PPP_IP);
2138	outlen = PPP_HDRLEN;
2139
2140	// ip
2141	bzero(outp, sizeof(struct ip));
2142	outp += sizeof(struct ip);
2143	outlen += sizeof(struct ip);
2144
2145	// udp
2146	bzero(outp, sizeof(struct udphdr));
2147	outp += sizeof(struct udphdr);
2148	outlen += sizeof(struct udphdr);
2149
2150	// bootp
2151	bzero(outp, sizeof(struct dhcp));
2152	PUTCHAR(BOOTREQUEST, outp);		// dp_op
2153	PUTCHAR(8, outp);				// dp_htype
2154	PUTCHAR(6, outp);				// dp_hlen
2155	PUTCHAR(0, outp);				// dp_hops
2156	PUTLONG(dhcp_ip_client_xid++, outp);		// dp_xid
2157	PUTSHORT(0, outp);				// dp_secs
2158	PUTSHORT(0, outp);				// dp_flags
2159	l = ntohl(ouraddr);
2160	PUTLONG(l, outp);				// dp_ciaddr
2161	PUTLONG(0, outp);				// dp_yiaddr
2162	PUTLONG(0, outp);				// dp_siaddr
2163	PUTLONG(0, outp);				// dp_giaddr
2164	PUTLONG(clientid, outp);		// dp_chaddr[0..3]
2165	PUTLONG(0, outp);				// dp_chaddr[4..7]
2166	PUTLONG(0, outp);				// dp_chaddr[8..11]
2167	PUTLONG(1, outp);				// dp_chaddr[12..15]
2168	outp += 64;						// dp_sname
2169	outp += 128;					// dp_file
2170	outlen += sizeof(struct dhcp);
2171
2172	// dhcp options
2173	PUTLONG(DHCP_COOKIE, outp);	// dhcp cookie
2174	outlen += 4;
2175
2176	PUTCHAR(DHCP_OPTION_MSG_TYPE, outp);	// dhcp message type
2177	PUTCHAR(1, outp);		// dhcp message type len
2178	PUTCHAR(DHCP_OPTION_MSG_TYPE_INFORM, outp);	// dhcp message type inform
2179	outlen += 3;
2180
2181	PUTCHAR(DHCP_OPTION_CLIENT_ID, outp);	// dhcp client id
2182	PUTCHAR(7, outp);		// dhcp client id len
2183	PUTCHAR(8, outp);		// htype
2184	PUTLONG(clientid, outp);	// client id
2185	PUTSHORT(0, outp);		// client id end
2186	outlen += 9;
2187
2188	PUTCHAR(DHCP_OPTION_PARAM_REQUEST_LIST, outp);	// dhcp param request list
2189	PUTCHAR(6, outp);		// dhcp param request list len
2190	PUTCHAR(DHCP_OPTION_DNS, outp);
2191	PUTCHAR(DHCP_OPTION_NETBIOS, outp);
2192	PUTCHAR(DHCP_OPTION_WINS, outp);
2193	PUTCHAR(DHCP_OPTION_SUBNET_MASK, outp);
2194	PUTCHAR(DHCP_OPTION_STATIC_ROUTE, outp);
2195	PUTCHAR(DHCP_OPTION_DOMAIN_NAME, outp);
2196	outlen += 8;
2197
2198	PUTCHAR(DHCP_OPTION_END, outp);		// end of options
2199	outlen ++;
2200
2201	pad = outlen%4;
2202	for (i = 0; i < pad && outlen < sizeof(outpacket_buf); i++) {
2203		PUTCHAR(0, outp);		// byte padding
2204		outlen ++;
2205	}
2206
2207	// build ip/udp header and send it...
2208	acsp_ipdata_send_packet(unit, outpacket_buf, outlen, ntohl(ouraddr), IPPORT_BOOTPC, INADDR_BROADCAST, IPPORT_BOOTPS, "DHCP packet inform");
2209
2210	dhcp_context->state = PLUGIN_SENDSTATE_WAITING_ACK;
2211	TIMEOUT(acsp_ipdata_timeout, dhcp_context, DHCP_TIMEOUT_VALUE);
2212}
2213
2214//------------------------------------------------------------
2215// acsp_ipdata_input
2216//------------------------------------------------------------
2217static void
2218acsp_ipdata_input(int unit, u_char *pkt, int len, u_int32_t ouraddr, u_int32_t hisaddr)
2219{
2220	struct dhcp_packet *dp;
2221
2222	dp = ALIGNED_CAST(struct dhcp_packet *)pkt;
2223
2224	/* check if we received a DHCP broadcast from a client */
2225	if (acsp_intercept_dhcp
2226		&& ntohl(dp->ip.ip_dst.s_addr) == INADDR_BROADCAST
2227		&& ntohs(dp->udp.uh_sport) == IPPORT_BOOTPC
2228		&& ntohs(dp->udp.uh_dport) == IPPORT_BOOTPS) {
2229		acsp_ipdata_input_server(unit, pkt, len, ouraddr, hisaddr);
2230		return;
2231	}
2232
2233	/* check if we received a DHCP reply from a server */
2234	if (acsp_use_dhcp
2235		&& dp->ip.ip_dst.s_addr == ouraddr
2236		&& ntohs(dp->udp.uh_sport) == IPPORT_BOOTPS
2237		&& ntohs(dp->udp.uh_dport) == IPPORT_BOOTPC) {
2238		acsp_ipdata_input_client(unit, pkt, len, ouraddr, hisaddr);
2239		return;
2240	}
2241}
2242
2243#define DHCP_NOTIFY_STORE_ON_TIMEOUT_COUNT 2
2244//------------------------------------------------------------
2245// acsp_ipdatre_timeout
2246//------------------------------------------------------------
2247static void
2248acsp_ipdata_timeout(void *arg)
2249{
2250	acsp_dhcp_context *context = (acsp_dhcp_context*)arg;
2251
2252	if (context->state == PLUGIN_SENDSTATE_WAITING_ACK) {
2253		if (context->retry_count++ < DHCP_MAX_RETRIES)
2254			acsp_ipdata_start_dhcp_client(context->unit, context->ouraddr.s_addr, context->hisaddr.s_addr);
2255		else {
2256			dbglog("No DHCP server replied\n");
2257			context->state = PLUGIN_STATE_DONE;
2258		}
2259
2260		if (context->retry_count == DHCP_NOTIFY_STORE_ON_TIMEOUT_COUNT) {
2261			notify(acspdhcpready_notifier, 0);
2262		}
2263	}
2264}
2265
2266//------------------------------------------------------------
2267// acsp_ipdata_up
2268//------------------------------------------------------------
2269static void
2270acsp_ipdata_up(int unit, u_int32_t ouraddr, u_int32_t hisaddr)
2271{
2272
2273	/* check if dhcp is enabled and acsp not running */
2274	if (acsp_use_dhcp && (acscp_protent.state(unit) != OPENED)) {
2275		/*
2276			allocate dhcp routes context
2277			we don't need to keep a context for the domain
2278		*/
2279		if ((dhcp_context = (acsp_dhcp_context*)malloc(sizeof(acsp_dhcp_context))) == 0) {
2280			error("ACSP plugin: no memory to allocate DHCP routes context\n");
2281			return;
2282		}
2283		bzero(dhcp_context, sizeof(acsp_dhcp_context));
2284		dhcp_context->unit = unit;
2285		dhcp_context->ouraddr.s_addr = ouraddr;
2286		dhcp_context->hisaddr.s_addr = hisaddr;
2287		dhcp_context->state = PLUGIN_STATE_INITIAL;
2288
2289		/* start dhcp client */
2290		acsp_ipdata_start_dhcp_client(unit, ouraddr, hisaddr);
2291	}
2292}
2293
2294//------------------------------------------------------------
2295// acsp_ipdata_down
2296//------------------------------------------------------------
2297static void
2298acsp_ipdata_down(int unit)
2299{
2300	if (acsp_use_dhcp) {
2301		/* cleanup route */
2302		if (dhcp_context) {
2303			acsp_plugin_remove_routes(dhcp_context->route);
2304			UNTIMEOUT(acsp_ipdata_timeout, dhcp_context);
2305			free(dhcp_context);
2306			dhcp_context = NULL;
2307		}
2308
2309	}
2310}
2311
2312//------------------------------------------------------------
2313// acsp_ipdata_print
2314//------------------------------------------------------------
2315static int
2316acsp_ipdata_print(pkt, plen, printer, arg)
2317    u_char *pkt;
2318    int plen;
2319    void (*printer) __P((void *, char *, ...));
2320    void *arg;
2321{
2322	u_char *p;
2323	int i, isbootp = 0, len = plen;
2324	struct dhcp *dp;
2325	struct dhcp_packet *packet;
2326	u_int32_t masklen, addrlen, addr, router, mask, l;
2327	char str[2048];
2328	u_int32_t cookie;
2329	char str2[16];
2330
2331	packet = ALIGNED_CAST(struct dhcp_packet *)pkt;
2332
2333	/* check if we received a DHCP broadcast from a client */
2334	isbootp =
2335		(ntohs(packet->udp.uh_sport) == IPPORT_BOOTPC || ntohs(packet->udp.uh_sport) == IPPORT_BOOTPS)
2336		&& (ntohs(packet->udp.uh_dport) == IPPORT_BOOTPS || ntohs(packet->udp.uh_dport) == IPPORT_BOOTPC);
2337
2338	if (!isbootp)
2339		return 0;
2340
2341	dp = (struct dhcp *)&packet->dhcp;
2342
2343	printer(arg, " <src addr %s>",  inet_ntoa(packet->ip.ip_src));
2344	printer(arg, " <dst addr %s>",  inet_ntoa(packet->ip.ip_dst));
2345
2346	if (dp->dp_op != BOOTREPLY && dp->dp_op != BOOTREQUEST) {
2347		printer(arg, " <bootp code invalid!>");
2348		return 0;
2349	}
2350
2351	printer(arg, " <BOOTP %s>", dp->dp_op == BOOTREQUEST ? "Request" : "Reply");
2352
2353	/* if superverbose debug, perform additional decoding onm the packet */
2354	if (debug > 1) {
2355		printer(arg, " <htype %d>", dp->dp_htype);
2356		printer(arg, " <hlen %d>", dp->dp_hlen);
2357		printer(arg, " <hops %d>", dp->dp_hops);
2358		printer(arg, " <xid %d>", dp->dp_xid);
2359		printer(arg, " <flags %d>", dp->dp_flags);
2360		printer(arg, " <client address %s>", inet_ntoa(dp->dp_ciaddr));
2361		printer(arg, " <your address %s>", inet_ntoa(dp->dp_yiaddr));
2362		printer(arg, " <server address %s>", inet_ntoa(dp->dp_siaddr));
2363		printer(arg, " <gateway address %s>", inet_ntoa(dp->dp_giaddr));
2364
2365		p = &dp->dp_chaddr[0];
2366		snprintf(str, sizeof(str), "%02x", p[0]);
2367		for (i = 1; i < 16; i++) {
2368			snprintf(str2, sizeof(str2), ":%02x", p[i]);
2369			strlcat(str, str2, sizeof(str));
2370		}
2371		printer(arg, " <hardware address %s>",  str);
2372		printer(arg, " <server host name \"%s\">", dp->dp_sname);
2373		printer(arg, " <boot file name \"%s\">", dp->dp_file);
2374	}
2375
2376	p = dp->dp_options;
2377    GETLONG(l, p);
2378	cookie = l;
2379	if (cookie != DHCP_COOKIE) {
2380		printer(arg, " <cookie invalid!>");
2381		return 0;
2382	}
2383	if (debug > 1)
2384		printer(arg, " <cookie 0x%x>", DHCP_COOKIE);
2385
2386	if (*p++ != DHCP_OPTION_MSG_TYPE || *p++ != 1 || (*p != DHCP_OPTION_MSG_TYPE_INFORM && *p != DHCP_OPTION_MSG_TYPE_ACK)) {
2387		printer(arg, " <type invalid!>");
2388		return 0;
2389	}
2390
2391	printer(arg, " <type %s>",  *p == DHCP_OPTION_MSG_TYPE_INFORM ? "INFORM" : "ACK");
2392	p++;
2393
2394	len = plen - sizeof(struct dhcp_packet) - 7;
2395	while (*p != DHCP_OPTION_END && len > 0) {
2396		u_int8_t optcode, optlen;
2397
2398		optcode = *p++;
2399		// check for pad option
2400		if (optcode == 0) {
2401			len--;
2402			continue;
2403		}
2404
2405		optlen = *p++;
2406		len-=2;
2407		if (len == 0) {
2408			printer(arg, " <option %d zero len!>", optcode);
2409			return 0;
2410		}
2411
2412		str[0] = 0;
2413		switch (optcode) {
2414			case DHCP_OPTION_HOST_NAME:
2415				memcpy(str, p, optlen);
2416				str[optlen] = 0;
2417				printer(arg, " <host name \"%s\">",  str);
2418				break;
2419			case DHCP_OPTION_VENDOR_CLASS_ID:
2420				memcpy(str, p, optlen);
2421				str[optlen] = 0;
2422				printer(arg, " <vendor class id \"%s\">",  str);
2423				break;
2424			case DHCP_OPTION_CLIENT_ID:
2425				snprintf(str, sizeof(str), "0x");
2426				for (i = 0; i < optlen; i++) {
2427					snprintf(str2, sizeof(str2), "%02x", p[i]);
2428					strlcat(str, str2, sizeof(str));
2429				}
2430				printer(arg, " <client id %s>",  str);
2431				break;
2432			case DHCP_OPTION_SERVER_ID:
2433				snprintf(str, sizeof(str), "0x");
2434				for (i = 0; i < optlen; i++) {
2435					snprintf(str2, sizeof(str2), "%02x", p[i]);
2436					strlcat(str, str2, sizeof(str));
2437				}
2438				printer(arg, " <server id %s>",  str);
2439				break;
2440			case DHCP_OPTION_LEASE_TIME:
2441                GETLONG(l, p);                  // Wcast-align fix - unaligned access
2442				printer(arg, " <lease time %d sec>",  l);
2443                p -= 4;
2444				break;
2445			case DHCP_OPTION_SUBNET_MASK:
2446				printer(arg, " <subnet mask %d.%d.%d.%d>",  p[0], p[1], p[2], p[3]);
2447				break;
2448			case DHCP_OPTION_DOMAIN_NAME:
2449				memcpy(str, p, optlen);
2450				str[optlen] = 0;
2451				printer(arg, " <domain name \"%s\">",  str);
2452				break;
2453			case DHCP_OPTION_STATIC_ROUTE:
2454				printer(arg, " <static routes");
2455				i = 0;
2456				while (i < optlen) {
2457					masklen = p[i];
2458					mask = 0xFFFFFFFF << (32 - masklen);
2459					addrlen = (masklen / 8);
2460					if (masklen % 8)
2461						addrlen++;
2462                    // Wcast-align fix - memcpy for unaligned access
2463                    memcpy(&l, &p[i+1], sizeof(l));
2464					addr = ntohl(l) & mask;
2465                    memcpy(&l, &p[i+1+addrlen], sizeof(l));
2466					router = ntohl(l);
2467					i += addrlen + 1 + sizeof(in_addr_t);
2468					printer(arg, " %d.%d.%d.%d/%d.%d.%d.%d/%d.%d.%d.%d",
2469						(addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr  >> 8) & 0xFF, addr & 0xFF,
2470						(mask >> 24) & 0xFF, (mask >> 16) & 0xFF, (mask >> 8) & 0xFF, mask & 0xFF,
2471						(router >> 24) & 0xFF, (router >> 16) & 0xFF, (router >> 8) & 0xFF, router & 0xFF);
2472				}
2473				printer(arg, ">");
2474				break;
2475			case DHCP_OPTION_PARAM_REQUEST_LIST:
2476				for (i = 0; i < optlen; i++) {
2477					snprintf(str2, sizeof(str2), " 0x%x", p[i]);
2478					strlcat(str, str2, sizeof(str));
2479				}
2480				printer(arg, " <parameters =%s>", str);
2481				break;
2482
2483			default:
2484				printer(arg, " <option %d>", optcode);
2485				break;
2486		}
2487
2488		p+=optlen;
2489		len-=optlen;
2490	}
2491
2492	/* if debug is superverbose, dump raw packet as well */
2493	if (debug > 1)
2494		return 0;
2495
2496	return plen;
2497}
2498
2499int
2500acsp_printpkt(p, plen, printer, arg)
2501u_char *p;
2502int plen;
2503void (*printer) __P((void *, char *, ...));
2504void *arg;
2505{
2506	int               len;
2507	u_int16_t         flags;
2508	int               slen;
2509    u_char           *pstart = p;
2510	acsp_packet      *pkt = ALIGNED_CAST(acsp_packet *)p;
2511    acsp_route_data	 *route_data;
2512	uint16_t          route_flags;
2513    u_int8_t          *ptr;
2514    acsp_domain_data  domain_data_aligned;
2515	int               domain_name_len;
2516	char              addr_str[INET_ADDRSTRLEN];
2517	char              mask_str[INET_ADDRSTRLEN];
2518	char              domain_name[255 + 1]; // plus null-termination
2519
2520	if(pkt && plen >= ACSP_HDR_SIZE) {
2521		len = ntohs(pkt->len);
2522		if (len < ACSP_HDR_SIZE)
2523			len = 0;
2524		else
2525			len -= ACSP_HDR_SIZE;
2526
2527		flags = ntohs(pkt->flags);
2528
2529	    if (pkt->type == CI_ROUTES) {
2530            route_data = ALIGNED_CAST(__typeof__(route_data))pkt->data;
2531			ACSP_PRINTPKT_PAYLOAD("CI_ROUTES");
2532            while (len >= sizeof(*route_data)) {
2533				route_flags = ntohs(route_data->flags);
2534                printer(arg, "\n    <route: address %s, mask %s, flags:%s%s>",
2535						addr2ascii(AF_INET, &route_data->address, sizeof(route_data->address), addr_str),
2536						addr2ascii(AF_INET, &route_data->mask, sizeof(route_data->mask), mask_str),
2537						((route_flags & ACSP_ROUTEFLAGS_PRIVATE) != 0)? " PRIVATE" : "",
2538						((route_flags & ACSP_ROUTEFLAGS_PUBLIC) != 0)? " PUBLIC" : "");
2539                len -= sizeof(*route_data);
2540                route_data++;
2541            }
2542			p = (__typeof__(p))route_data;
2543		} else if (pkt->type == CI_DOMAINS) {
2544            ptr = pkt->data;
2545			ACSP_PRINTPKT_PAYLOAD("CI_DOMAINS");
2546            while (len >= sizeof(acsp_domain_data)) {
2547                memcpy(&domain_data_aligned, ptr, sizeof(acsp_domain_data));      // Wcast-align fix - memcpy for unaligned move
2548                slen = ntohs(domain_data_aligned.len);
2549				domain_name_len = MIN(slen, sizeof(domain_name));
2550				if (slen) {
2551					memcpy(domain_name, ((acsp_domain_data *)(void*)ptr)->name, domain_name_len);
2552				}
2553				domain_name[domain_name_len] = 0;
2554				if (domain_data_aligned.server) {
2555					printer(arg, "\n    <domain: name %s, server %s>",
2556							domain_name,
2557							addr2ascii(AF_INET, &domain_data_aligned.server, sizeof(domain_data_aligned.server), addr_str));
2558				} else {
2559					printer(arg, "\n    <domain: name %s>",
2560							domain_name);
2561				}
2562                len -= (ACSP_DOMAIN_DATA_HDRSIZE + slen);
2563                ptr += (ACSP_DOMAIN_DATA_HDRSIZE + slen);
2564            }
2565			p = (__typeof__(p))ptr;
2566		} else {
2567			ACSP_PRINTPKT_PAYLOAD(NULL);
2568			p = pkt->data;
2569		}
2570		return (p - pstart);
2571	}
2572	return 0;
2573}
2574
2575