1/*
2 * Copyright (c) 1999-2013, 2011 Apple 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 * dhcp_options.c
26 * - routines to parse and access dhcp options
27 *   and create new dhcp option areas
28 * - handles overloaded areas as well as vendor-specific options
29 *   that are encoded using the RFC 2132 encoding
30 */
31
32/*
33 * Modification History
34 *
35 * November 23, 1999	Dieter Siegmund (dieter@apple)
36 * - created from objective C version (dhcpOptions.m)
37 * - generalized code so that the file, sname and vendor specific
38 *   areas could be parsed using the same routines
39 * - cleanly separated functions that deal with existing option areas
40 *   (dhcpol_*) from those that create new option areas (dhpoa_*).
41 */
42#include "dhcp.h"
43
44#include <unistd.h>
45#include <stdlib.h>
46#include <stdio.h>
47#include <sys/types.h>
48#include <netinet/in.h>
49#include <arpa/inet.h>
50#include <strings.h>
51
52#include "rfc_options.h"
53#include "gen_dhcp_types.h"
54
55#include "dhcp_options.h"
56#include "gen_dhcp_parse_table.h"
57#include "util.h"
58#include "DNSNameList.h"
59#include "nbo.h"
60#include "cfutil.h"
61#include <SystemConfiguration/SCPrivate.h>
62
63/*
64 * Module: dhcpoa (dhcp options area)
65 *
66 * Purpose:
67 *   Types and functions to create new dhcp option areas.
68 */
69
70#define DHCPOA_MAGIC	0x11223344
71
72
73#define MAX_TAG (sizeof(dhcptag_info_table) / sizeof(dhcptag_info_table[0]))
74
75
76__private_extern__ const uint8_t	rfc_magic[] = RFC_OPTIONS_MAGIC;
77
78
79/*
80 * Functions: dhcptag_*, dhcptype_*
81 *
82 * Purpose:
83 *   Functions to deal with dhcp option tag and type information.
84 */
85
86/*
87 * Function: dhcptype_info
88 *
89 * Purpose:
90 *   Return the type information for the given type.
91 */
92const dhcptype_info_t *
93dhcptype_info(dhcptype_t type)
94{
95    if (type > dhcptype_last_e)
96	return (NULL);
97    return dhcptype_info_table + type;
98}
99
100/*
101 * Function: dhcptype_from_str
102 *
103 * Purpose:
104 *   Convert from a string to the appropriate internal representation
105 *   for the given type.  Calls the appropriate strto<type> function.
106 */
107boolean_t
108dhcptype_from_str(const char * str, int type, void * buf, int * len_p,
109		  dhcpo_err_str_t * err)
110{
111    const dhcptype_info_t * 	type_info = dhcptype_info(type);
112
113    if (err)
114	err->str[0] = '\0';
115
116    if (type_info == NULL) {
117	if (err) {
118	    snprintf(err->str, sizeof(err->str), "type %d unknown", type);
119	}
120	return (FALSE);
121    }
122
123    if (*len_p < type_info->size) {
124	if (err) {
125	    snprintf(err->str, sizeof(err->str),
126		     "type %s buffer too small (%d < %d)",
127		     type_info->name, *len_p, type_info->size);
128	}
129	return (FALSE);
130    }
131
132    switch (type) {
133      case dhcptype_bool_e: {
134	  long l = strtol(str, 0, 0);
135	  *len_p = type_info->size;
136	  *((uint8_t *)buf) = ((l == 0) ? 0 : 1);
137	  break;
138      }
139      case dhcptype_uint8_e: {
140	  unsigned long ul = strtoul(str, 0, 0);
141	  *len_p = type_info->size;
142	  if (ul > 255) {
143	      if (err) {
144		  snprintf(err->str, sizeof(err->str),
145			   "value %ld too large for %s",
146			   ul, type_info->name);
147	      }
148	      return (FALSE);
149	  }
150	  *((uint8_t *)buf) = ul;
151	  break;
152      }
153      case dhcptype_uint16_e: {
154	  unsigned long ul = strtoul(str, 0, 0);
155	  unsigned short us = ul;
156
157	  if (ul > 65535) {
158	      if (err) {
159		  snprintf(err->str, sizeof(err->str),
160			   "value %ld too large for %s",
161			   ul, type_info->name);
162	      }
163	      return (FALSE);
164	  }
165	  net_uint16_set(buf, us);
166	  *len_p = type_info->size;
167	  break;
168      }
169      case dhcptype_int32_e: {
170	  int32_t l = (int32_t)strtol(str, 0, 0);
171	  net_uint32_set(buf, l);
172	  *len_p = type_info->size;
173	  break;
174      }
175      case dhcptype_uint32_e: {
176	  uint32_t l = (uint32_t)strtoul(str, 0, 0);
177	  net_uint32_set(buf, l);
178	  *len_p = type_info->size;
179	  break;
180      }
181      case dhcptype_ip_e: {
182	  struct in_addr ip;
183
184	  if (inet_aton(str, &ip) == 0) {
185	      if (err) {
186		  snprintf(err->str, sizeof(err->str), "%s not valid ip",
187			   str);
188	      }
189	      return (FALSE);
190	  }
191	  bcopy(&ip, buf, sizeof(ip));
192	  *len_p = type_info->size;
193	  break;
194      }
195      case dhcptype_string_e: {
196	  int len = (int)strlen(str);
197	  if (*len_p < len) {
198	      if (err) {
199		  snprintf(err->str, sizeof(err->str), "string too long");
200	      }
201	      return (FALSE);
202	  }
203	  if (len)
204	      bcopy(str, buf, len);
205	  *len_p = len;
206	  break;
207      }
208      case dhcptype_dns_namelist_e: {
209	  (void)DNSNameListBufferCreate(&str, 1, buf, len_p);
210	  if (*len_p == 0) {
211	      strlcpy(err->str, "no DNS names added", sizeof(err->str));
212	      return (FALSE);
213	  }
214	  break;
215      }
216      default:
217	  if (err) {
218	      snprintf(err->str, sizeof(err->str), "type %s not yet supported",
219		       type_info->name);
220	  }
221	  return (FALSE);
222    }
223    return (TRUE);
224}
225
226/*
227 * Function: dhcptype_from_strlist
228 *
229 * Purpose:
230 *   Convert from a string list to the appropriate internal representation
231 *   for the given type.
232 */
233boolean_t
234dhcptype_from_strlist(const char * * strlist, int strlist_count,
235		      int type, void * buf, int * len_p,
236		      dhcpo_err_str_t * err)
237{
238    if (err != NULL) {
239	err->str[0] = '\0';
240    }
241
242    switch (type) {
243      case dhcptype_dns_namelist_e: {
244	  (void)DNSNameListBufferCreate(strlist, strlist_count, buf, len_p);
245	  if (*len_p == 0) {
246	      strlcpy(err->str, "no DNS names added", sizeof(err->str));
247	      return (FALSE);
248	  }
249	  break;
250      }
251      default:
252	  if (err != NULL) {
253	      snprintf(err->str, sizeof(err->str), "type %d not yet supported",
254		       type);
255	  }
256	  return (FALSE);
257    }
258    return (TRUE);
259}
260
261/*
262 * Function: dhcptype_to_str
263 *
264 * Purpose:
265 *   Give a string representation to the given type.
266 */
267boolean_t
268dhcptype_to_str(char * tmp, size_t maxtmplen,
269		const void * opt, int len, int type,
270		dhcpo_err_str_t * err)
271{
272    switch (type) {
273    case dhcptype_bool_e:
274	snprintf(tmp, maxtmplen, "%d", *((boolean_t *)opt));
275	break;
276    case dhcptype_uint8_e:
277	snprintf(tmp, maxtmplen, "%d", *((uint8_t *)opt));
278	break;
279    case dhcptype_uint16_e:
280	snprintf(tmp, maxtmplen, "%d", net_uint16_get(opt));
281	break;
282    case dhcptype_int32_e:
283	snprintf(tmp, maxtmplen, "%d", net_uint32_get(opt));
284	break;
285    case dhcptype_uint32_e:
286	snprintf(tmp, maxtmplen, "%u", net_uint32_get(opt));
287	break;
288    case dhcptype_ip_e:
289	snprintf(tmp, maxtmplen, IP_FORMAT, IP_LIST(opt));
290	break;
291    case dhcptype_string_e:
292	if (len < maxtmplen) {
293	    strncpy(tmp, opt, len);
294	    tmp[len] = '\0';
295	}
296	else {
297	    snprintf(err->str, sizeof(err->str),
298		     "type dhcptype_string_e: string too long");
299	    return (FALSE);
300	}
301	break;
302    default:
303	if (err) {
304	    snprintf(err->str, sizeof(err->str),
305		     "type %d: not supported", type);
306	    return (FALSE);
307	}
308    }
309    return (TRUE);
310}
311
312void
313dhcptype_print_cfstr_simple(CFMutableStringRef str, dhcptype_t type,
314			    const void * opt, int option_len)
315{
316    const uint8_t *	option = opt;
317
318    switch (type) {
319      case dhcptype_bool_e:
320	  STRING_APPEND(str, "%s", *option ? "TRUE" : "FALSE");
321	  break;
322
323      case dhcptype_ip_e:
324	  STRING_APPEND(str, IP_FORMAT, IP_LIST(opt));
325	  break;
326
327      case dhcptype_string_e: {
328	  STRING_APPEND(str, "%.*s", option_len, (char *)option);
329	  break;
330      }
331
332      case dhcptype_opaque_e:
333	STRING_APPEND(str, "\n");
334	print_data_cfstr(str, option, option_len);
335	break;
336
337      case dhcptype_uint8_e:
338	STRING_APPEND(str, "0x%x", *option);
339	break;
340
341      case dhcptype_uint16_e:
342	STRING_APPEND(str, "0x%x", net_uint16_get(option));
343	break;
344
345      case dhcptype_int32_e:
346      case dhcptype_uint32_e:
347	STRING_APPEND(str, "0x%x", net_uint32_get(option));
348	break;
349
350      case dhcptype_dns_namelist_e: {
351	  int			i;
352	  const char * *	namelist;
353	  int			namelist_length = 0;
354
355	  namelist = DNSNameListCreate(option, option_len, &namelist_length);
356	  STRING_APPEND(str, "{");
357	  if (namelist != NULL) {
358	      for (i = 0; i < namelist_length; i++) {
359		  STRING_APPEND(str, "%s%s", (i == 0) ? "" : ", ",
360			  namelist[i]);
361	      }
362	      free(namelist);
363	  }
364	  STRING_APPEND(str, "}");
365	  break;
366      }
367
368      case dhcptype_none_e:
369      default:
370	break;
371    }
372    return;
373}
374
375void
376dhcptype_fprint_simple(FILE * f, dhcptype_t type,
377		       const void * opt, int option_len)
378{
379    CFMutableStringRef		str;
380
381    str = CFStringCreateMutable(NULL, 0);
382    dhcptype_print_cfstr_simple(str, type, opt, option_len);
383    SCPrint(TRUE, f, CFSTR("%@"), str);
384    CFRelease(str);
385    return;
386}
387
388/*
389 * Function: dhcptype_print_simple
390 * Purpose:
391 *   Display (to stdout) an option with a simple type.
392 */
393void
394dhcptype_print_simple(dhcptype_t type, const void * opt, int option_len)
395{
396    dhcptype_fprint_simple(stdout, type, opt, option_len);
397    return;
398}
399
400void
401dhcptype_print_cfstr(CFMutableStringRef str,
402		     dhcptype_t type, const void * option, int option_len)
403{
404    const dhcptype_info_t * 	type_info = dhcptype_info(type);
405
406    if (type_info && type_info->multiple_of != dhcptype_none_e) {
407	int 			i;
408	int 			number;
409	const void *		offset;
410	int 			size;
411	const dhcptype_info_t * subtype_info;
412
413	subtype_info = dhcptype_info(type_info->multiple_of);
414	if (subtype_info == NULL)
415	    return;
416	size = subtype_info->size;
417	number = option_len / size;
418	STRING_APPEND(str, "{");
419	for (i = 0, offset = option; i < number; i++) {
420	    if (i != 0)
421		STRING_APPEND(str, ", ");
422	    dhcptype_print_cfstr_simple(str, type_info->multiple_of,
423					offset, size);
424	    offset += size;
425	}
426	STRING_APPEND(str, "}");
427    }
428    else {
429	dhcptype_print_cfstr_simple(str, type, option, option_len);
430    }
431    return;
432}
433
434void
435dhcptype_fprint(FILE * f, dhcptype_t type, const void * option, int option_len)
436{
437    CFMutableStringRef		str;
438
439    str = CFStringCreateMutable(NULL, 0);
440    dhcptype_print_cfstr(str, type, option, option_len);
441    SCPrint(TRUE, f, CFSTR("%@"), str);
442    CFRelease(str);
443    return;
444}
445
446/*
447 * Function: dhcptype_print
448 * Purpose:
449 *   Display (to stdout) an option with the given type.
450 */
451void
452dhcptype_print(dhcptype_t type, const void * option, int option_len)
453{
454    dhcptype_fprint(stdout, type, option, option_len);
455    return;
456}
457
458static boolean_t
459dhcptag_print_cfstr(CFMutableStringRef str, const void * vopt)
460{
461    const dhcptag_info_t * entry;
462    const uint8_t *	opt = vopt;
463    uint8_t 		tag = opt[TAG_OFFSET];
464    uint8_t 		option_len = opt[LEN_OFFSET];
465    const uint8_t * 	option = opt + OPTION_OFFSET;
466
467    entry = dhcptag_info(tag);
468    if (entry == NULL)
469	return (FALSE);
470    {
471	const dhcptype_info_t * type = dhcptype_info(entry->type);
472
473	if (type == NULL) {
474	    STRING_APPEND(str, "unknown type %d\n", entry->type);
475	    return (FALSE);
476	}
477	STRING_APPEND(str, "%s (%s): ", entry->name, type->name);
478	if (tag == dhcptag_dhcp_message_type_e)
479	    STRING_APPEND(str, "%s ", dhcp_msgtype_names(*option));
480	dhcptype_print_cfstr(str, entry->type, option, option_len);
481	STRING_APPEND(str, "\n");
482    }
483    return (TRUE);
484}
485
486boolean_t
487dhcptag_fprint(FILE * f, const void * vopt)
488{
489    boolean_t			printed;
490    CFMutableStringRef		str;
491
492    str = CFStringCreateMutable(NULL, 0);
493    printed = dhcptag_print_cfstr(str, vopt);
494    CFRelease(str);
495    return (printed);
496}
497
498/*
499 * Function: dhcptag_print
500 * Purpose:
501 *   Display (to stdout) the given option.
502 * Returns:
503 *   TRUE it could be displayed, FALSE otherwise.
504 */
505boolean_t
506dhcptag_print(const void * vopt)
507{
508    return (dhcptag_fprint(stdout, vopt));
509}
510
511/*
512 * Function: dhcptag_info
513 *
514 * Purpose:
515 *   Return the tag information for the give tag.
516 */
517const dhcptag_info_t *
518dhcptag_info(dhcptag_t tag)
519{
520    if (tag >= MAX_TAG)
521	return (NULL);
522    return dhcptag_info_table + tag;
523}
524
525/*
526 * Function: dhcptag_with_name
527 *
528 * Purpose:
529 *   Given a name, return a corresponding tag.  This comes from
530 *   the table, as well as the default name of the option number itself.
531 */
532
533dhcptag_t
534dhcptag_with_name(const char * name)
535{
536    dhcptag_t 		i;
537    unsigned int 	v;
538
539    for (i = 0; i < MAX_TAG; i++) {
540	if (dhcptag_info_table[i].name) {
541	    if (strcmp(dhcptag_info_table[i].name, name) == 0)
542		return (i);
543	}
544    }
545    if (strncmp(name, "option_", 7) == 0) {
546	v = (unsigned int)strtoul(name + 7, NULL, 10);
547    }
548    else {
549	v = (unsigned int)strtoul(name, NULL, 10);
550    }
551    if (v <= MAX_TAG) {
552	return (v);
553    }
554    return (-1);
555}
556
557/*
558 * Function: dhcptag_name
559 * Purpose:
560 *   Return the name of the tag.
561 */
562const char *
563dhcptag_name(int tag)
564{
565    const dhcptag_info_t * info;
566
567    info = dhcptag_info(tag);
568    if (info == NULL)
569	return (NULL);
570    return (info->name);
571}
572
573/*
574 * Function: dhcptag_from_strlist
575 *
576 * Purpose:
577 *   Convert from a string list using the appropriate
578 *   type conversion for the tag's type.
579 */
580boolean_t
581dhcptag_from_strlist(const char * * slist, int num,
582		     int tag, void * buf, int * len_p,
583		     dhcpo_err_str_t * err)
584{
585    int				i;
586    int				n_per_type;
587    const dhcptag_info_t *	tag_info;
588    const dhcptype_info_t * 	type_info;
589    const dhcptype_info_t * 	base_type_info;
590
591    if (err)
592	err->str[0] = '\0';
593    tag_info = dhcptag_info(tag);
594    if (tag_info == NULL) {
595	if (err)
596	    snprintf(err->str, sizeof(err->str), "tag %d unknown", tag);
597	return (FALSE);
598    }
599
600    type_info = dhcptype_info(tag_info->type);
601    if (type_info == NULL) {
602	if (err)
603	    snprintf(err->str, sizeof(err->str), "tag %d type %d unknown", tag,
604		     tag_info->type);
605	return (FALSE);
606    }
607
608    if (type_info->string_list == FALSE) {
609	return (dhcptype_from_str(slist[0], tag_info->type,
610				  buf, len_p, err));
611    }
612    if (type_info->multiple_of == dhcptype_none_e) {
613	return (dhcptype_from_strlist(slist, num, tag_info->type,
614				      buf, len_p, err));
615    }
616    base_type_info = dhcptype_info(type_info->multiple_of);
617    if (base_type_info == NULL) {
618	if (err)
619	    snprintf(err->str, sizeof(err->str), "tag %d base type %d unknown", tag,
620		     type_info->multiple_of);
621	return (FALSE);
622    }
623
624    n_per_type = type_info->size / base_type_info->size;
625    if (num & (n_per_type - 1)) {
626	if (err)
627	    snprintf(err->str, sizeof(err->str), "type %s not a multiple of %d",
628		     type_info->name, n_per_type);
629	return (FALSE);
630    }
631
632    if ((num * base_type_info->size) > *len_p) /* truncate if necessary */
633	num = *len_p / base_type_info->size;
634
635    for (i = 0, *len_p = 0; i < num; i++) {
636	int 		l;
637
638	l = base_type_info->size;
639	if (dhcptype_from_str(slist[i], type_info->multiple_of, buf, &l,
640			      err) == FALSE)
641	    return (FALSE);
642	buf += l;
643	*len_p += l;
644    }
645    return (TRUE);
646}
647
648/*
649 * Function: dhcptag_to_str
650 *
651 * Purpose:
652 *   Give a string representation to the given tag.
653 * Note:
654 *   For tags whose type is a multiple of a type, this routine
655 *   only returns the string representation for the first
656 *   occurrence.
657 */
658boolean_t
659dhcptag_to_str(char * tmp, size_t tmplen, int tag, const void * opt,
660	       int len, dhcpo_err_str_t * err)
661{
662    const dhcptag_info_t * 	tag_info;
663    const dhcptype_info_t * 	type_info;
664    dhcptype_t			type;
665
666    tag_info = dhcptag_info(tag);
667    if (tag_info == NULL)
668	return (FALSE);
669    type_info = dhcptype_info(tag_info->type);
670    if (type_info->multiple_of == dhcptype_none_e)
671	type = tag_info->type;
672    else
673	type = type_info->multiple_of;
674    return (dhcptype_to_str(tmp, tmplen, opt, len, type, err));
675}
676
677/*
678 * Functions: dhcpol_*
679 *
680 * Purpose:
681 *   Routines to parse/access existing options buffers.
682 */
683boolean_t
684dhcpol_add(dhcpol_t * list, void * element)
685{
686    return (ptrlist_add((ptrlist_t *)list, element));
687}
688
689int
690dhcpol_count(dhcpol_t * list)
691{
692    return (ptrlist_count((ptrlist_t *)list));
693}
694
695void *
696dhcpol_element(dhcpol_t * list, int i)
697{
698    return (ptrlist_element((ptrlist_t *)list, i));
699}
700
701void
702dhcpol_init(dhcpol_t * list)
703{
704    ptrlist_init((ptrlist_t *)list);
705}
706
707void
708dhcpol_free(dhcpol_t * list)
709{
710    ptrlist_free((ptrlist_t *)list);
711}
712
713boolean_t
714dhcpol_concat(dhcpol_t * list, dhcpol_t * extra)
715{
716    return (ptrlist_concat((ptrlist_t *)list, (ptrlist_t *)extra));
717}
718
719/*
720 * Function: dhcpol_parse_buffer
721 *
722 * Purpose:
723 *   Parse the given buffer into DHCP options, returning the
724 *   list of option pointers in the given dhcpol_t.
725 *   Parsing continues until we hit the end of the buffer or
726 *   the end tag.
727 */
728boolean_t
729dhcpol_parse_buffer(dhcpol_t * list, void * buffer, int length,
730		    dhcpo_err_str_t * err)
731{
732    int		len;
733    uint8_t *	scan;
734    uint8_t	tag;
735
736    if (err)
737	err->str[0] = '\0';
738
739    dhcpol_init(list);
740
741    len = length;
742    tag = dhcptag_pad_e;
743    for (scan = (uint8_t *)buffer; tag != dhcptag_end_e && len > 0; ) {
744
745	tag = scan[TAG_OFFSET];
746
747	switch (tag) {
748	  case dhcptag_end_e:
749	      dhcpol_add(list, scan); /* remember that it was terminated */
750	      scan++;
751	      len--;
752	      break;
753	  case dhcptag_pad_e: /* ignore pad */
754	      scan++;
755	      len--;
756	      break;
757	  default: {
758	      uint8_t	option_len = scan[LEN_OFFSET];
759
760	      dhcpol_add(list, scan);
761	      len -= (option_len + 2);
762	      scan += (option_len + 2);
763	      break;
764	  }
765	}
766    }
767    if (len < 0) {
768	/* ran off the end */
769	if (err)
770	    snprintf(err->str, sizeof(err->str), "parse failed near tag %d", tag);
771	dhcpol_free(list);
772	return (FALSE);
773    }
774    return (TRUE);
775}
776
777/*
778 * Function: dhcpol_find
779 *
780 * Purpose:
781 *   Finds the first occurence of the given option, and returns its
782 *   length and the option data pointer.
783 *
784 *   The optional start parameter allows this function to
785 *   return the next start point so that successive
786 *   calls will retrieve the next occurence of the option.
787 *   Before the first call, *start should be set to 0.
788 */
789void *
790dhcpol_find(dhcpol_t * list, int tag, int * len_p, int * start)
791{
792    int 	i = 0;
793
794    if (tag == dhcptag_end_e || tag == dhcptag_pad_e)
795	return (NULL);
796
797    if (start)
798	i = *start;
799
800    for (; i < dhcpol_count(list); i++) {
801	uint8_t * option = dhcpol_element(list, i);
802
803	if (option[TAG_OFFSET] == tag) {
804	    if (len_p)
805		*len_p = option[LEN_OFFSET];
806	    if (start)
807		*start = i + 1;
808	    return (option + OPTION_OFFSET);
809	}
810    }
811    return (NULL);
812}
813
814/*
815 * Function: dhcpol_find_with_length
816 * Purpose:
817 *   Find a DHCP option with the specified tag that is at least as long
818 *   as the passed in min_length value.  Return NULL if such an option
819 *   does not exist.
820 */
821void *
822dhcpol_find_with_length(dhcpol_t * options, dhcptag_t tag, int min_length)
823{
824    int		real_length;
825    void * 	val;
826
827    val = dhcpol_find(options, tag, &real_length, NULL);
828    if (val == NULL) {
829	return (NULL);
830    }
831    if (real_length < min_length) {
832	return (NULL);
833    }
834    return (val);
835}
836
837/*
838 * Function: dhcpol_option_copy
839 *
840 * Purpose:
841 *   Accumulate all occurences of the given option into a
842 *   malloc'd buffer, and return its length.  Used to get
843 *   all occurrences of a particular option in a single
844 *   data area.
845 * Note:
846 *   Use free() to free the returned data area.
847 */
848void *
849dhcpol_option_copy(dhcpol_t * list, int tag, int * len_p)
850{
851    int 	i;
852    void *	data = NULL;
853    int		data_len = 0;
854
855    if (tag == dhcptag_end_e || tag == dhcptag_pad_e)
856	return (NULL);
857
858    for (i = 0; i < dhcpol_count(list); i++) {
859	uint8_t * option = dhcpol_element(list, i);
860
861	if (option[TAG_OFFSET] == tag) {
862	    int len = option[LEN_OFFSET];
863
864	    if (data == NULL) {
865		data = malloc(len);
866	    }
867	    else {
868		data = reallocf(data, data_len + len);
869	    }
870	    bcopy(option + OPTION_OFFSET, data + data_len, len);
871	    data_len += len;
872	}
873    }
874    *len_p = data_len;
875    return (data);
876}
877
878/*
879 * Function: dhcpol_parse_packet
880 *
881 * Purpose:
882 *    Parse the option areas in the DHCP packet.
883 *    Verifies that the packet has the right magic number,
884 *    then parses and accumulates the option areas.
885 *    First the pkt->dp_options is parsed.  If that contains
886 *    the overload option, it parses pkt->dp_file if specified,
887 *    then parses pkt->dp_sname if specified.
888 */
889boolean_t
890dhcpol_parse_packet(dhcpol_t * options, struct dhcp * pkt, int len,
891		    dhcpo_err_str_t * err)
892{
893    dhcpol_init(options);	/* make sure it's empty */
894
895    if (err)
896	err->str[0] = '\0';
897
898    if (len < (sizeof(*pkt) + RFC_MAGIC_SIZE)) {
899	if (err) {
900	    snprintf(err->str, sizeof(err->str), "packet is too short: %d < %d",
901		    len, (int)sizeof(*pkt) + RFC_MAGIC_SIZE);
902	}
903	return (FALSE);
904    }
905    if (bcmp(pkt->dp_options, rfc_magic, RFC_MAGIC_SIZE)) {
906	if (err)
907	    snprintf(err->str, sizeof(err->str), "missing magic number");
908	return (FALSE);
909    }
910    if (dhcpol_parse_buffer(options, pkt->dp_options + RFC_MAGIC_SIZE,
911			    len - sizeof(*pkt) - RFC_MAGIC_SIZE, err) == FALSE)
912	return (FALSE);
913    { /* get overloaded options */
914	uint8_t *	overload;
915	int		overload_len;
916
917	overload = (uint8_t *)dhcpol_find(options, dhcptag_option_overload_e,
918					  &overload_len, NULL);
919	if (overload && overload_len == 1) { /* has overloaded options */
920	    dhcpol_t	extra;
921
922	    dhcpol_init(&extra);
923	    if (*overload == DHCP_OVERLOAD_FILE
924		|| *overload == DHCP_OVERLOAD_BOTH) {
925		if (dhcpol_parse_buffer(&extra, pkt->dp_file,
926					 sizeof(pkt->dp_file), NULL)) {
927		    dhcpol_concat(options, &extra);
928		    dhcpol_free(&extra);
929		}
930	    }
931	    if (*overload == DHCP_OVERLOAD_SNAME
932		|| *overload == DHCP_OVERLOAD_BOTH) {
933		if (dhcpol_parse_buffer(&extra, pkt->dp_sname,
934					 sizeof(pkt->dp_sname), NULL)) {
935		    dhcpol_concat(options, &extra);
936		    dhcpol_free(&extra);
937		}
938	    }
939	}
940    }
941    return (TRUE);
942}
943
944/*
945 * Function: dhcpol_parse_vendor
946 *
947 * Purpose:
948 *   Given a set of options, find the vendor specific option(s)
949 *   and parse all of them into a single option list.
950 *
951 * Return value:
952 *   TRUE if vendor specific options existed and were parsed succesfully,
953 *   FALSE otherwise.
954 */
955boolean_t
956dhcpol_parse_vendor(dhcpol_t * vendor, dhcpol_t * options,
957		    dhcpo_err_str_t * err)
958{
959    dhcpol_t		extra;
960    boolean_t		ret = FALSE;
961    int 		start = 0;
962
963    if (err)
964	err->str[0] = '\0';
965
966    dhcpol_init(vendor);
967    dhcpol_init(&extra);
968
969    for (;;) {
970	void *		data;
971	int		len;
972
973	data = dhcpol_find(options, dhcptag_vendor_specific_e, &len, &start);
974	if (data == NULL) {
975	    break; /* out of for */
976	}
977
978	if (dhcpol_parse_buffer(&extra, data, len, err) == FALSE) {
979	    goto failed;
980	}
981
982	if (dhcpol_concat(vendor, &extra) == FALSE) {
983	    if (err)
984		snprintf(err->str, sizeof(err->str),
985			 "dhcpol_concat() failed at %d\n", start);
986	    goto failed;
987	}
988	dhcpol_free(&extra);
989	ret = TRUE;
990    }
991    if (ret == FALSE) {
992	if (err)
993	    strlcpy(err->str, "missing vendor specific options",
994		    sizeof(err->str));
995    }
996    return (ret);
997
998 failed:
999    dhcpol_free(vendor);
1000    dhcpol_free(&extra);
1001    return (FALSE);
1002}
1003
1004void
1005dhcpol_print_cfstr(CFMutableStringRef str, dhcpol_t * list)
1006{
1007    int 		i;
1008
1009    STRING_APPEND(str, "Options count is %d\n", dhcpol_count(list));
1010    for (i = 0; i < dhcpol_count(list); i++) {
1011	uint8_t * option = dhcpol_element(list, i);
1012
1013	if (dhcptag_print_cfstr(str, option) == FALSE) {
1014	    STRING_APPEND(str, "undefined tag %d len %d\n", option[TAG_OFFSET],
1015			  option[LEN_OFFSET]);
1016	}
1017    }
1018    return;
1019}
1020
1021void
1022dhcpol_fprint(FILE * f, dhcpol_t * list)
1023{
1024    CFMutableStringRef	str;
1025
1026    str = CFStringCreateMutable(NULL, 0);
1027    dhcpol_print_cfstr(str, list);
1028    SCPrint(TRUE, f, CFSTR("%@"), str);
1029    CFRelease(str);
1030    return;
1031}
1032
1033void
1034dhcpol_print(dhcpol_t * list)
1035{
1036    dhcpol_fprint(stdout, list);
1037    return;
1038}
1039
1040
1041int
1042dhcpol_count_params(dhcpol_t * options, const uint8_t * tags, int size)
1043{
1044    int		i;
1045    int		param_count = 0;
1046
1047    for (i = 0; i < size; i++) {
1048	if (dhcpol_find(options, tags[i], NULL, NULL) != NULL)
1049	    param_count++;
1050    }
1051    return (param_count);
1052}
1053
1054
1055/*
1056 * Module: dhcpoa
1057 *
1058 * Purpose:
1059 *   Types and functions to create new dhcp option areas.
1060 */
1061
1062/*
1063 * Function: dhcpoa_{init_common, init_no_end, init}
1064 *
1065 * Purpose:
1066 *   Initialize an option area structure so that it can be used
1067 *   in calling the dhcpoa_* routines.
1068 */
1069
1070static void
1071dhcpoa_init_common(dhcpoa_t * oa_p, void * buffer, int size, int reserve)
1072{
1073    bzero(buffer, size);	/* fill option area with pad tags */
1074
1075    bzero(oa_p, sizeof(*oa_p));
1076    oa_p->oa_magic = DHCPOA_MAGIC;
1077    oa_p->oa_buffer = buffer;
1078    oa_p->oa_size = size;
1079    oa_p->oa_reserve = reserve;
1080}
1081
1082void
1083dhcpoa_init_no_end(dhcpoa_t * oa_p, void * buffer, int size)
1084{
1085    dhcpoa_init_common(oa_p, buffer, size, 0);
1086    return;
1087}
1088
1089int
1090dhcpoa_size(dhcpoa_t * oa_p)
1091{
1092    return (oa_p->oa_size);
1093}
1094
1095void
1096dhcpoa_init(dhcpoa_t * oa_p, void * buffer, int size)
1097{
1098    /* initialize the area, reserve space for the end tag */
1099    dhcpoa_init_common(oa_p, buffer, size, 1);
1100    return;
1101}
1102
1103dhcpoa_ret_t
1104dhcpoa_vendor_add(dhcpoa_t * oa_p, dhcpoa_t * vendor_oa_p,
1105		  dhcptag_t tag, int len, void * option)
1106{
1107    int			freespace;
1108    dhcpoa_ret_t	ret = dhcpoa_success_e;
1109
1110    oa_p->oa_err.str[0] = '\0';
1111
1112    if (len > (DHCP_OPTION_SIZE_MAX - OPTION_OFFSET)) {
1113	snprintf(vendor_oa_p->oa_err.str, sizeof(vendor_oa_p->oa_err.str),
1114		"tag %d option %d > %d", tag, len,
1115		DHCP_OPTION_SIZE_MAX - OPTION_OFFSET);
1116	return (dhcpoa_failed_e);
1117    }
1118    freespace = dhcpoa_freespace(vendor_oa_p) - OPTION_OFFSET;
1119    if (freespace < len) {
1120	/* add the vendor-specific options to the packet to make room */
1121	ret = dhcpoa_add(oa_p, dhcptag_vendor_specific_e,
1122			 dhcpoa_used(vendor_oa_p),
1123			 dhcpoa_buffer(vendor_oa_p));
1124	if (ret != dhcpoa_success_e) {
1125	    snprintf(vendor_oa_p->oa_err.str, sizeof(vendor_oa_p->oa_err.str),
1126		    "tag %d option %d > %d", tag, len, freespace);
1127	    goto failed;
1128	}
1129	freespace = dhcpoa_freespace(oa_p) - OPTION_OFFSET;
1130	if (freespace > dhcpoa_size(vendor_oa_p)) {
1131	    freespace = dhcpoa_size(vendor_oa_p);
1132	}
1133	dhcpoa_init_no_end(vendor_oa_p, dhcpoa_buffer(vendor_oa_p),
1134			   freespace);
1135    }
1136    ret = dhcpoa_add(vendor_oa_p, tag, len, option);
1137
1138 failed:
1139    return (ret);
1140}
1141
1142
1143/*
1144 * Function: dhcpoa_add
1145 *
1146 * Purpose:
1147 *   Add an option to the option area.
1148 */
1149dhcpoa_ret_t
1150dhcpoa_add(dhcpoa_t * oa_p, dhcptag_t tag, int len, const void * option)
1151{
1152
1153    oa_p->oa_err.str[0] = '\0';
1154
1155    if (len > DHCP_OPTION_SIZE_MAX) {
1156	snprintf(oa_p->oa_err.str, sizeof(oa_p->oa_err.str),
1157		 "tag %d option %d > %d", tag, len, DHCP_OPTION_SIZE_MAX);
1158	return (dhcpoa_failed_e);
1159    }
1160
1161    if (oa_p->oa_magic != DHCPOA_MAGIC) {
1162	strlcpy(oa_p->oa_err.str, "dhcpoa_t not initialized - internal error!!!",
1163		sizeof(oa_p->oa_err.str));
1164	return (dhcpoa_failed_e);
1165    }
1166
1167    if (oa_p->oa_end_tag) {
1168	strlcpy(oa_p->oa_err.str, "attempt to add data after end tag",
1169		sizeof(oa_p->oa_err.str));
1170	return (dhcpoa_failed_e);
1171    }
1172
1173    switch (tag) {
1174      case dhcptag_end_e:
1175	if ((oa_p->oa_offset + 1) > oa_p->oa_size) {
1176	    /* this can't happen since we're careful to leave space */
1177	    snprintf(oa_p->oa_err.str, sizeof(oa_p->oa_err.str),
1178		     "can't add end tag %d > %d",
1179		     oa_p->oa_offset + oa_p->oa_reserve, oa_p->oa_size);
1180	    return (dhcpoa_failed_e);
1181	}
1182	((uint8_t *)oa_p->oa_buffer)[oa_p->oa_offset + TAG_OFFSET] = tag;
1183	oa_p->oa_offset++;
1184	oa_p->oa_end_tag = TRUE;
1185	break;
1186
1187      case dhcptag_pad_e:
1188	/* 1 for pad tag */
1189	if ((oa_p->oa_offset + oa_p->oa_reserve + 1) > oa_p->oa_size) {
1190	    snprintf(oa_p->oa_err.str, sizeof(oa_p->oa_err.str),
1191		     "can't add pad tag %d > %d",
1192		     oa_p->oa_offset + oa_p->oa_reserve + 1, oa_p->oa_size);
1193	    return (dhcpoa_full_e);
1194	}
1195	((u_char *)oa_p->oa_buffer)[oa_p->oa_offset + TAG_OFFSET] = tag;
1196	oa_p->oa_offset++;
1197	break;
1198
1199      default:
1200	/* 2 for tag/len */
1201	if ((oa_p->oa_offset + len + 2 + oa_p->oa_reserve) > oa_p->oa_size) {
1202	    snprintf(oa_p->oa_err.str, sizeof(oa_p->oa_err.str),
1203		     "can't add tag %d (%d > %d)", tag,
1204		     oa_p->oa_offset + len + 2 + oa_p->oa_reserve,
1205		     oa_p->oa_size);
1206	    return (dhcpoa_full_e);
1207	}
1208	((uint8_t *)oa_p->oa_buffer)[oa_p->oa_offset + TAG_OFFSET] = tag;
1209	((uint8_t *)oa_p->oa_buffer)[oa_p->oa_offset + LEN_OFFSET] = len;
1210	if (len) {
1211	    bcopy(option, (u_char *)oa_p->oa_buffer
1212		  + (OPTION_OFFSET + oa_p->oa_offset), len);
1213	}
1214	oa_p->oa_prev_last = oa_p->oa_last;
1215	oa_p->oa_last = oa_p->oa_offset;
1216	oa_p->oa_offset += len + OPTION_OFFSET;
1217	break;
1218    }
1219    oa_p->oa_option_count++;
1220    return (dhcpoa_success_e);
1221}
1222
1223/*
1224 * Function: dhcpoa_add_from_strlist
1225 *
1226 * Purpose:
1227 *   Convert the string list into an option with the specified tag.
1228 * Note:
1229 *   This only works for type representations that we know about.
1230 */
1231dhcpoa_ret_t
1232dhcpoa_add_from_strlist(dhcpoa_t * oa_p, dhcptag_t tag,
1233			const char * * strlist, int strlist_len)
1234{
1235    int 		len;
1236    char		tmp[DHCP_OPTION_SIZE_MAX];
1237
1238    len = sizeof(tmp);
1239
1240    if (dhcptag_from_strlist(strlist, strlist_len, tag, tmp, &len,
1241			     &oa_p->oa_err) == FALSE) {
1242	return (dhcpoa_failed_e);
1243    }
1244    return (dhcpoa_add(oa_p, tag, len, tmp));
1245}
1246
1247/*
1248 * Function: dhcpoa_add_from_str
1249 *
1250 * Purpose:
1251 *   Convert the string into an option with the specified tag.
1252 */
1253dhcpoa_ret_t
1254dhcpoa_add_from_str(dhcpoa_t * oa_p, dhcptag_t tag,
1255		    const char * str)
1256{
1257    return (dhcpoa_add_from_strlist(oa_p, tag, &str, 1));
1258}
1259
1260/*
1261 * Function: dhcpoa_add_dhcpmsg
1262 *
1263 * Purpose:
1264 *   Add a dhcp message option to the option area.
1265 */
1266dhcpoa_ret_t
1267dhcpoa_add_dhcpmsg(dhcpoa_t * oa_p, dhcp_msgtype_t msgtype)
1268{
1269    char m = (char)msgtype;
1270    return (dhcpoa_add(oa_p, dhcptag_dhcp_message_type_e,
1271			sizeof(m), &m));
1272}
1273
1274/*
1275 * Function: dhcpoa_err
1276 *
1277 * Purpose:
1278 *   Return an error message for the last error that occcurred.
1279 */
1280char *
1281dhcpoa_err(dhcpoa_t * oa_p)
1282{
1283    if (oa_p == NULL || oa_p->oa_magic != DHCPOA_MAGIC)
1284	return ("<bad parameter>");
1285    return (oa_p->oa_err.str);
1286}
1287
1288
1289int
1290dhcpoa_used(dhcpoa_t * oa_p)
1291{
1292    if (oa_p == NULL || oa_p->oa_magic != DHCPOA_MAGIC)
1293	return 0;
1294    return (oa_p->oa_offset);
1295}
1296
1297int
1298dhcpoa_freespace(dhcpoa_t * oa_p)
1299{
1300    int	freespace;
1301
1302    if (oa_p == NULL || oa_p->oa_magic != DHCPOA_MAGIC) {
1303	return 0;
1304    }
1305    freespace = oa_p->oa_size - oa_p->oa_offset - oa_p->oa_reserve;
1306    if (freespace < 0) {
1307	freespace = 0;
1308    }
1309    return (freespace);
1310}
1311
1312int
1313dhcpoa_count(dhcpoa_t * oa_p)
1314{
1315    if (oa_p == NULL || oa_p->oa_magic != DHCPOA_MAGIC)
1316	return 0;
1317    return (oa_p->oa_option_count);
1318}
1319
1320void *
1321dhcpoa_buffer(dhcpoa_t * oa_p)
1322{
1323    if (oa_p == NULL || oa_p->oa_magic != DHCPOA_MAGIC)
1324      return (NULL);
1325    return (oa_p->oa_buffer);
1326}
1327
1328#ifdef TEST_DHCP_OPTIONS
1329char test_empty[] = {
1330    99, 130, 83, 99,
1331    255,
1332};
1333
1334char test_simple[] = {
1335    99, 130, 83, 99,
1336    1, 4, 255, 255, 252, 0,
1337    3, 4, 17, 202, 40, 1,
1338    255,
1339};
1340
1341char test_vendor[] = {
1342    99, 130, 83, 99,
1343    1, 4, 255, 255, 252, 0,
1344    3, 4, 17, 202, 40, 1,
1345    43, 6, 1, 4, 1, 2, 3, 4,
1346    43, 6, 1, 4, 1, 2, 3, 4,
1347    255,
1348};
1349char test_overload_sname[] = {
1350    99, 130, 83, 99,
1351    dhcptag_option_overload_e, 1, DHCP_OVERLOAD_SNAME,
1352    255,
1353};
1354char test_overload_file[] = {
1355    99, 130, 83, 99,
1356    dhcptag_option_overload_e, 1, DHCP_OVERLOAD_FILE,
1357    255,
1358};
1359char test_overload_both[] = {
1360    99, 130, 83, 99,
1361    dhcptag_option_overload_e, 1, DHCP_OVERLOAD_BOTH,
1362    255,
1363};
1364
1365char test_no_end[] = {
1366    0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36,
1367    0x04, 0xc0, 0xa8, 0x01, 0x01, 0x33, 0x04, 0x80,
1368    0x00, 0x80, 0x00, 0x01, 0x04, 0xff, 0xff, 0xff,
1369    0x00, 0x03, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x06,
1370    0x0c, 0x18, 0x1a, 0xa3, 0x21, 0x18, 0x1a, 0xa3,
1371    0x20, 0x18, 0x5e, 0xa3, 0x21, 0x00, 0x00, 0x00,
1372    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1373    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1374};
1375
1376char test_too_short[] = {
1377    0x1
1378};
1379struct test {
1380    char * 		name;
1381    char *		data;
1382    int			len;
1383    boolean_t		result;
1384};
1385
1386struct test tests[] = {
1387    { "empty", test_empty, sizeof(test_empty), TRUE },
1388    { "simple", test_simple, sizeof(test_simple), TRUE },
1389    { "vendor", test_vendor, sizeof(test_vendor), TRUE },
1390    { "no_end", test_no_end, sizeof(test_no_end), TRUE },
1391    { "too_short", test_too_short, sizeof(test_too_short), FALSE },
1392    { NULL, NULL, 0, FALSE },
1393};
1394
1395struct test overload_tests[] = {
1396    { "overload sname", test_overload_sname, sizeof(test_overload_sname),TRUE },
1397    { "overload file", test_overload_file, sizeof(test_overload_file), TRUE },
1398    { "overload both", test_overload_both, sizeof(test_overload_both), TRUE },
1399    { NULL, NULL, 0, FALSE },
1400};
1401
1402char test_string_253[253] = "test string 253 characters long (zero padded)";
1403
1404static char buf[2048];
1405static char vend_buf[255];
1406
1407int
1408main()
1409{
1410    int 		i;
1411    dhcpo_err_str_t	error;
1412    dhcpol_t 		options;
1413    struct dhcp * 	pkt = (struct dhcp *)buf;
1414    dhcpol_t		vendor_options;
1415
1416    dhcpol_init(&options);
1417    dhcpol_init(&vendor_options);
1418
1419    for (i = 0; tests[i].name; i++) {
1420	printf("\nTest %d: %s: ", i, tests[i].name);
1421	bcopy(tests[i].data, pkt->dp_options, tests[i].len);
1422	if (dhcpol_parse_packet(&options, pkt,
1423				sizeof(*pkt) + tests[i].len,
1424				&error) != tests[i].result) {
1425	    printf("FAILED\n");
1426	    if (tests[i].result == TRUE) {
1427		printf("error message returned was %s\n", error.str);
1428	    }
1429	}
1430	else {
1431	    printf("PASSED\n");
1432	    if (tests[i].result == FALSE) {
1433		printf("error message returned was %s\n", error.str);
1434	    }
1435	}
1436	dhcpol_print(&options);
1437	dhcpol_free(&options);
1438    }
1439    bcopy(test_simple + RFC_MAGIC_SIZE, pkt->dp_sname,
1440	  sizeof(test_simple) - RFC_MAGIC_SIZE);
1441    bcopy(test_no_end + RFC_MAGIC_SIZE, pkt->dp_file,
1442	  sizeof(test_no_end) - RFC_MAGIC_SIZE);
1443    for (i = 0; overload_tests[i].name; i++) {
1444	bcopy(overload_tests[i].data, pkt->dp_options,
1445	      overload_tests[i].len);
1446	printf("\nOption overload test %d: %s: ", i, overload_tests[i].name);
1447	if (dhcpol_parse_packet(&options, pkt,
1448				sizeof(*pkt) + overload_tests[i].len,
1449				&error) != tests[i].result) {
1450	    printf("FAILED\n");
1451	    if (overload_tests[i].result == TRUE) {
1452		printf("error message returned was %s\n", error.str);
1453	    }
1454	}
1455	else {
1456	    printf("PASSED\n");
1457	    if (overload_tests[i].result == FALSE) {
1458		printf("error message returned was %s\n", error.str);
1459	    }
1460	}
1461	dhcpol_print(&options);
1462	dhcpol_free(&options);
1463    }
1464
1465    printf("\nTesting dhcpoa\n");
1466    {
1467	struct in_addr	iaddr;
1468	dhcpoa_t	opts;
1469	dhcpoa_t	vend_opts;
1470	dhcpo_err_str_t err;
1471	char *		str;
1472
1473	dhcpoa_init(&opts, buf, sizeof(buf));
1474	dhcpoa_init_no_end(&vend_opts, vend_buf, sizeof(vend_buf));
1475
1476	iaddr.s_addr = inet_addr("255.255.252.0");
1477	if (dhcpoa_add(&opts, dhcptag_subnet_mask_e, sizeof(iaddr),
1478		       &iaddr) == dhcpoa_failed_e) {
1479	    printf("couldn't add subnet mask, %s\n", dhcpoa_err(&opts));
1480	    exit(1);
1481	}
1482	str = "17.202.40.1";
1483	if (dhcpoa_add_from_str(&opts, dhcptag_router_e, str)
1484	    != dhcpoa_success_e) {
1485	    printf("couldn't add router, %s\n", dhcpoa_err(&opts));
1486	    exit(1);
1487	}
1488	if (dhcpoa_add_from_str(&opts, dhcptag_host_name_e, "siegdi5")
1489	    != dhcpoa_success_e) {
1490	    printf("couldn't add hostname tag, %s\n", dhcpoa_err(&opts));
1491	    exit(1);
1492	}
1493	if (dhcpoa_add_from_str(&opts, dhcptag_domain_name_e, "apple.com")
1494	    != dhcpoa_success_e) {
1495	    printf("couldn't add domain name tag, %s\n", dhcpoa_err(&opts));
1496	    exit(1);
1497	}
1498	{
1499	    const char * 	domain_names[] = {
1500		"euro.apple.com",
1501		"eng.apple.com",
1502		"foo.bar",
1503		"thisisprettylongdontyouthink.foo.bar",
1504		"thisisprettylongdontyouthink.foo.bar.apple.com",
1505		"thisisprettylongdontyouthink.foo.bar.com",
1506		"a.foo.bar"
1507	    };
1508	    uint8_t		search_buf[DHCP_OPTION_SIZE_MAX];
1509	    int			len = sizeof(search_buf);
1510
1511	    if (dhcptag_from_strlist(domain_names, 7,
1512				     dhcptag_domain_search_e, search_buf,
1513				     &len, &err) == FALSE) {
1514		printf("couldn't get domain search option: %s", err.str);
1515	    }
1516	    else if (dhcpoa_add(&opts, dhcptag_domain_search_e,
1517				len, search_buf) != dhcpoa_success_e) {
1518		printf("couldn't add domain search tag, %s\n",
1519		       dhcpoa_err(&opts));
1520		exit(1);
1521	    }
1522	}
1523	for (i = 0; i < 253; i++) {
1524	    if (dhcpoa_vendor_add(&opts, &vend_opts, i+1, i,
1525				  test_string_253) != dhcpoa_success_e) {
1526		printf("couldn't add vendor option, %s\n",
1527		       dhcpoa_err(&vend_opts));
1528		break;
1529	    }
1530	}
1531	if (dhcpoa_used(&vend_opts) > 0) {
1532	    if (dhcpoa_add(&opts, dhcptag_vendor_specific_e,
1533			   dhcpoa_used(&vend_opts), dhcpoa_buffer(&vend_opts))
1534		!= dhcpoa_success_e) {
1535		printf("couldn't add vendor options, %s\n", dhcpoa_err(&opts));
1536		exit(1);
1537	    }
1538	}
1539	if (dhcpoa_add(&opts, dhcptag_end_e, 0, 0) != dhcpoa_success_e) {
1540	    printf("couldn't add end tag, %s\n", dhcpoa_err(&opts));
1541	    exit(1);
1542	}
1543	if (dhcpol_parse_buffer(&options, buf, sizeof(buf), &err) == FALSE) {
1544	    printf("parse buffer failed, %s\n", err.str);
1545	    exit(1);
1546	}
1547	dhcpol_print(&options);
1548	{
1549	    struct in_addr * iaddr;
1550	    iaddr = (struct in_addr *)
1551		dhcpol_find(&options, dhcptag_router_e, 0, 0);
1552	    if (iaddr == NULL) {
1553		printf("can't find router option\n");
1554	    }
1555	    else {
1556		printf("Found router option %s\n", inet_ntoa(*iaddr));
1557	    }
1558	}
1559	if (dhcpol_parse_vendor(&vendor_options, &options, NULL)) {
1560	    printf("vendor parsed ok\n");
1561	}
1562	else {
1563	    printf("parse vendor failed\n");
1564	}
1565	dhcpol_free(&options);
1566	dhcpol_free(&vendor_options);
1567    }
1568    exit(0);
1569}
1570#endif /* TEST_DHCP_OPTIONS */
1571