1/*
2 * Copyright (c) 1999 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 * macnc_options.c
26 * - handle dhcp/bootp options specific to the macNC
27 */
28
29/*
30 * Modification History:
31 *
32 * December 15, 1997	Dieter Siegmund (dieter@apple)
33 * - created
34 * November 19, 1999 	Dieter Siegmund (dieter@apple)
35 * - converted to regular C
36 */
37#import <unistd.h>
38#import <stdlib.h>
39#import <stdio.h>
40#import <sys/types.h>
41#import <netinet/in.h>
42#import <arpa/inet.h>
43#import <string.h>
44#import "rfc_options.h"
45#import "macnc_options.h"
46
47typedef struct {
48    macNCtype_t		type;
49    dhcptype_info_t	info;
50} macNCtype_info_t;
51
52static const macNCtype_info_t macNCtype_info_table[] = {
53    { macNCtype_pstring_e, 	{ 1, dhcptype_none_e, "PString" } },
54    { macNCtype_afp_path_e, 	{ 0, dhcptype_none_e, "AFP path" } },
55    { macNCtype_afp_password_e, { 8, dhcptype_none_e, "AFP password" } },
56};
57
58static const int macNCtype_info_size = sizeof(macNCtype_info_table)
59     / sizeof(macNCtype_info_t);
60
61typedef struct {
62    macNCtag_t		tag;
63    dhcptag_info_t	info;
64} macNCtag_info_t;
65
66static const macNCtag_info_t macNCtag_info_table[] = {
67 { macNCtag_client_version_e, {dhcptype_uint32_e, "macNC_client_version" } },
68 { macNCtag_client_info_e, { dhcptype_opaque_e, "macNC_client_info" } },
69 { macNCtag_server_version_e, { dhcptype_uint32_e, "macNC_server_version" } },
70 { macNCtag_server_info_e, { dhcptype_opaque_e, "macNC_server_info" } },
71 { macNCtag_user_name_e, { dhcptype_string_e, "macNC_user_name" } },
72 { macNCtag_password_e, { macNCtype_afp_password_e, "macNC_password" } },
73 { macNCtag_shared_system_file_e,
74       { macNCtype_afp_path_e, "macNC_shared_system_file" } },
75 { macNCtag_private_system_file_e,
76       { macNCtype_afp_path_e, "macNC_private_system_file" } },
77 { macNCtag_page_file_e,
78       { macNCtype_afp_path_e, "macNC_page_file" } },
79 { macNCtag_MacOS_machine_name_e,
80       { dhcptype_string_e, "macNC_MacOS_machine_name" } },
81 { macNCtag_shared_system_shadow_file_e,
82       { macNCtype_afp_path_e, "macNC_shared_system_shadow_file" } },
83 { macNCtag_private_system_shadow_file_e,
84       { macNCtype_afp_path_e, "macNC_private_system_shadow_file" } },
85};
86
87static int macNCtag_info_size = sizeof(macNCtag_info_table)
88     / sizeof(macNCtag_info_t);
89
90static const dhcptype_info_t *
91macNCtype_info(int type)
92{
93    int 			i;
94    const dhcptype_info_t * 	t;
95
96    for (i = 0; i < macNCtype_info_size; i++) {
97	if (type == macNCtype_info_table[i].type)
98	    return (&macNCtype_info_table[i].info);
99    }
100    t = dhcptype_info(type);
101    if (t)
102	return (t);
103    return (NULL);
104}
105
106static const dhcptag_info_t *
107macNCtag_info(int tag)
108{
109    int 			i;
110    const dhcptag_info_t * 	t;
111
112    for (i = 0; i < macNCtag_info_size; i++) {
113	if (tag == macNCtag_info_table[i].tag)
114	    return (&macNCtag_info_table[i].info);
115    }
116    t = dhcptag_info(tag);
117    if (t)
118	return (t);
119    return (NULL);
120}
121
122boolean_t
123macNCopt_str_to_type(const char * str,
124		     int type, void * buf, int * len_p,
125		     dhcpo_err_str_t * err)
126{
127    const dhcptype_info_t * 	type_info = macNCtype_info(type);
128
129    if (err)
130	err->str[0] = '\0';
131
132    switch (type) {
133      case macNCtype_afp_password_e: {
134	  int len = strlen(str);
135	  if (*len_p < AFP_PASSWORD_LEN) {
136	      if (err)
137		  snprintf(err->str, sizeof(err->str),
138			   "%s: buffer too small (%d < %d)",
139			   type_info->name, *len_p, AFP_PASSWORD_LEN);
140	      return (FALSE);
141	  }
142	  if (len > AFP_PASSWORD_LEN) {
143	      if (err)
144		  snprintf(err->str, sizeof(err->str),
145			   "%s: string too large (%d > %d)",
146			   type_info->name, len, AFP_PASSWORD_LEN);
147	      return (FALSE);
148	  }
149	  *len_p = AFP_PASSWORD_LEN;
150	  bzero(buf, AFP_PASSWORD_LEN);
151	  strncpy((char *)buf, str, len);
152        }
153	break;
154      case macNCtype_pstring_e: {
155	  int len = strlen(str);
156	  if (*len_p < (len + 1)) {
157	      if (err)
158		  snprintf(err->str, sizeof(err->str),
159			   "%s: buffer too small (%d < %d)",
160			   type_info->name, *len_p, len + 1);
161	      return (FALSE);
162	  }
163	  ((u_char *)buf)[0] = len;			/* string length */
164	  bcopy(str, buf + 1, len);
165	  *len_p = len + 1;
166        }
167	break;
168      case macNCtype_afp_path_e:
169	if (err)
170	    snprintf(err->str, sizeof(err->str),
171		     "%s: not supported", type_info->name);
172	return (FALSE);
173	break;
174      default:
175	return (dhcptype_from_str(str, type, buf, len_p, err));
176	break;
177    }
178    return (TRUE);
179}
180
181static void
182S_replace_separators(u_char * buf, int len, u_char sep, u_char new_sep)
183{
184    int i;
185
186    for (i = 0; i < len; i++) {
187	if (buf[i] == sep)
188	    buf[i] = new_sep;
189    }
190    return;
191}
192
193boolean_t
194macNCopt_encodeAFPPath(struct in_addr iaddr, uint16_t port,
195		       const char * volname, uint32_t dirID,
196		       uint8_t pathtype, const char * pathname,
197		       char separator, void * buf,
198		       int * len_p, dhcpo_err_str_t * err)
199{
200    void * 	buf_p = buf;
201    int 	l;
202
203    l = strlen(volname) + strlen(pathname);
204    if (l > AFP_PATH_LIMIT) {
205	if (err)
206	    snprintf(err->str, sizeof(err->str),
207		     "volume/path name length %d > %d-byte limit", l,
208		     AFP_PATH_LIMIT);
209	return (FALSE);
210    }
211
212    if ((l + AFP_PATH_OVERHEAD) > *len_p) {
213	if (err)
214	    snprintf(err->str, sizeof(err->str),
215		     "buffer too small: %d > %d", l + AFP_PATH_OVERHEAD,
216		    *len_p);
217	return (FALSE);
218    }
219    *len_p = l + AFP_PATH_OVERHEAD;			/* option len */
220
221    *((struct in_addr *)buf_p) = iaddr;		/* ip */
222    buf_p += sizeof(iaddr);
223
224    *((u_short *)buf_p) = port;			/* port */
225    buf_p += sizeof(port);
226
227    l = strlen(volname);			/* VolName */
228    *((u_char *)buf_p) = l;
229    buf_p++;
230    if (l)
231	bcopy(volname, (u_char *)buf_p, l);
232    buf_p += l;
233
234    *((uint32_t *)buf_p) = dirID;			/* DirID */
235    buf_p += sizeof(dirID);
236
237    *((uint8_t *)buf_p) = pathtype;		/* AFPPathType */
238    buf_p += sizeof(pathtype);
239
240    l = strlen(pathname);			/* PathName */
241    *((uint8_t *)buf_p) = l;
242    buf_p++;
243    if (l) {
244	bcopy(pathname, (u_char *)buf_p, l);
245	S_replace_separators(buf_p, l, separator, AFP_PATH_SEPARATOR);
246    }
247
248    return (TRUE);
249}
250
251static void
252print_pstring(const uint8_t * option)
253
254{
255    int 	i;
256    int		len = option[0];
257
258    for (i = 0; i < len; i++) {
259	char ch = option[1 + i];
260	printf("%c", ch ? ch : '.');
261    }
262}
263
264static void
265macNC_print_type(dhcptype_t type, void * opt, int option_len)
266{
267    int 	offset;
268    uint8_t * 	option = opt;
269
270    switch (type) {
271      case macNCtype_afp_password_e:
272	if (option_len != AFP_PASSWORD_LEN)
273	    printf("bad password field\n");
274	else {
275	    char buf[9];
276	    strncpy(buf, (char *)opt, AFP_PASSWORD_LEN);
277	    buf[8] = '\0';
278	    printf("%s", buf);
279	}
280	break;
281      case macNCtype_afp_path_e:
282	offset = 0;
283	printf("(");
284
285	dhcptype_print(dhcptype_ip_e, option, option_len);
286	offset += 4;
287
288	printf(", ");
289	dhcptype_print(dhcptype_uint16_e, option + offset, option_len);
290	offset += 2;
291
292	printf(", ");
293	print_pstring(option + offset);
294	offset += option[offset] + 1;
295
296	printf(", ");
297	dhcptype_print(dhcptype_uint32_e, option + offset, option_len);
298	offset += 4;
299
300	printf(", ");
301	dhcptype_print(dhcptype_uint8_e, option + offset, option_len);
302	offset += 1;
303
304	printf(", ");
305	print_pstring(option + offset);
306	printf(")");
307	break;
308
309      case macNCtype_pstring_e: {
310	print_pstring(option);
311	break;
312      }
313      default:
314	dhcptype_print(type, option, option_len);
315	break;
316    }
317    return;
318}
319
320boolean_t
321macNC_print_option(void * vopt)
322{
323    u_char *    		opt = vopt;
324    u_char 			tag = opt[TAG_OFFSET];
325    u_char 			option_len = opt[LEN_OFFSET];
326    u_char * 			option = opt + OPTION_OFFSET;
327    const dhcptag_info_t * 	entry;
328
329    entry = macNCtag_info(tag);
330    if (entry == NULL)
331	return (FALSE);
332    {
333	const dhcptype_info_t * type = macNCtype_info(entry->type);
334
335	if (type == NULL) {
336	    printf("unknown type %d\n", entry->type);
337	    return (FALSE);
338	}
339	printf("%s (%s): ", entry->name, type->name);
340	if (tag == dhcptag_dhcp_message_type_e)
341	    printf("%s ", dhcp_msgtype_names(*option));
342	macNC_print_type(entry->type, option, option_len);
343	printf("\n");
344    }
345    return (TRUE);
346}
347
348void
349macNCopt_print(dhcpol_t * list)
350{
351    int 		i;
352
353    printf("Options count is %d\n", dhcpol_count(list));
354    for (i = 0; i < dhcpol_count(list); i++) {
355	unsigned char * option = dhcpol_element(list, i);
356	if (macNC_print_option(option) == FALSE)
357	    printf("undefined tag %d len %d\n", option[TAG_OFFSET],
358		   option[LEN_OFFSET]);
359    }
360}
361
362#ifdef TEST_MACNC_OPTIONS
363
364/**
365 **
366 ** Testing 1 2 3
367 **
368 **/
369u_char test[] =
370{
371    dhcptag_subnet_mask_e,
372    4,
373    255, 255, 252, 0,
374
375    dhcptag_router_e,
376    12,
377    17, 202, 40, 1,
378    17, 202, 41, 1,
379    17, 202, 42, 1,
380
381    dhcptag_domain_name_server_e,
382    4,
383    17, 128, 100, 12,
384
385    dhcptag_host_name_e,
386    7,
387    's', 'i', 'e', 'g', 'd', 'i', '7',
388
389    dhcptag_pad_e,
390
391    dhcptag_all_subnets_local_e,
392    1,
393    0,
394
395    dhcptag_vendor_specific_e,
396    24,
397    't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 't', 'e', 's', 't',
398    234, 212, 0, 1, 2, 3, 4, 5, 6, 7,
399
400    macNCtag_user_name_e,
401    10,
402    'M', 'a', 'c', 'N', 'C', ' ', '#', ' ', '1', '9',
403
404    macNCtag_shared_system_file_e,
405    29,
406    17, 202, 40, 191,
407    0x20, 0x00,
408    4, 'a', 'b', 'c', 'd',
409    0, 0, 0, 0,
410    2,
411    12, 0, 'e', 0, 'f', 0, 'g', 'h', 'i', 0, 'j', 'k', 'l',
412
413    dhcptag_end_e,
414};
415
416#if 0
417u_char test[] = {
4180x01, 0x04, 0xff, 0x00, 0x00, 0x00, 0x03, 0x04, 0x0f, 0x03, 0x03, 0x09, 0x06,
4190x04, 0x0f, 0x03, 0x03, 0x09, 0xe8, 0x09, 0x08, 0x6d, 0x61, 0x63, 0x6e, 0x63, 0x30, 0x30, 0x30,
4200xed, 0x09, 0x08, 0x6d, 0x61, 0x63, 0x6e, 0x63, 0x30, 0x30, 0x30, 0xe9, 0x09, 0x08, 0x74, 0x65,
4210x73, 0x74, 0x69, 0x6e, 0x67, 0x32, 0xea, 0x18, 0x0f, 0x03, 0x03, 0x05, 0x02, 0x24, 0x00, 0x06,
4220x64, 0x75, 0x63, 0x61, 0x74, 0x73, 0x09, 0x31, 0x35, 0x2e, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x37,
4230xeb, 0x18, 0x0f, 0x03, 0x03, 0x05, 0x02, 0x24, 0x00, 0x06, 0x64, 0x75, 0x63, 0x61, 0x74, 0x73,
4240x09, 0x31, 0x35, 0x2e, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x37, 0xec, 0x18, 0x0f, 0x03, 0x03, 0x05,
4250x02, 0x24, 0x00, 0x06, 0x64, 0x75, 0x63, 0x61, 0x74, 0x73, 0x09, 0x31, 0x35, 0x2e, 0x33, 0x2e,
4260x33, 0x2e, 0x31, 0x37, 0xff,
427};
428#endif
429
430int
431main()
432{
433    dhcpo_err_str_t 	err;
434    dhcpol_t 		list;
435
436    dhcpol_init(&list);
437    if (dhcpol_parse_buffer(&list, test, sizeof(test), &err) == FALSE) {
438	printf("parse test failed, %s\n", err.str);
439	exit(1);
440    }
441    macNCopt_print(&list);
442    exit(0);
443#if 0
444    {
445	struct in_addr	iaddr;
446	int i = sizeof(iaddr);
447
448	if ([options str:"17.202.42.129" ToType:dhcptype_ip_e
449	     Buffer:(void *)&iaddr Length:&i] == FALSE) {
450	    printf("conversion failed %s\n", [options errString]);
451	}
452	else {
453	    printf("ip address should be 17.202.42.129: ");
454	    [options printType:dhcptype_ip_e Size:i Option:(void *)&iaddr
455	     Length:i];
456	    printf("\n");
457	}
458    }
459    {
460	unsigned char buf[32] = "Mac NC #33";
461	unsigned char buf2[34];
462	int len = sizeof(buf2);
463
464	if ([options str:buf ToType:macNCtype_pstring_e Buffer:buf2
465	     Length:&len] == FALSE) {
466	    printf("conversion failed %s\n", [options errString]);
467	}
468	else {
469	    printf("macNCtype string should be %s:", buf);
470	    [options printType:macNCtype_pstring_e Size:0 Option:buf2
471	     Length:len];
472	    printf("\n");
473	}
474    }
475    {
476	struct in_addr	iaddr[10];
477	int l = sizeof(iaddr);
478	u_char * strList[] = { "17.202.40.1", "17.202.41.1", "17.202.42.1",
479			     "17.202.43.1" };
480	int num = sizeof(strList) / sizeof(*strList);
481
482	if ([options strList:strList Number:num Tag:dhcptag_router_e
483	     Buffer:(void *)iaddr Length:&l] == FALSE) {
484	    printf("conversion failed %s\n", [options errString]);
485	}
486	else {
487	    [options printType:dhcptype_ip_mult_e Size:4 Option:(void *)iaddr
488	     Length:l];
489	    printf("\n");
490	}
491    }
492    {
493	u_char buf[100];
494	u_char * strList[] = { "17.86.91.2", "0x100", "0", "greatVolumeName",
495				   "/spectacular/path/name/eh" };
496	int l = sizeof(buf);
497	int num = sizeof(strList) / sizeof(*strList);
498
499	if ([macNCOptions strList:strList Number:num
500	     Tag:macNCtag_system_path_shared_e Buffer:(void *)buf Length:&l
501	     ErrorString:[options errString]] == FALSE) {
502	    printf("conversion failed %s\n", [options errString]);
503	}
504	else {
505	    printf("conversion OK\n");
506	    [options printType:macNCtype_afp_path_e Size:0 Option:(void *)buf
507	     Length:l];
508	    printf("\n");
509	}
510    }
511    {
512	u_char buf[100];
513	int l = sizeof(buf);
514	struct in_addr iaddr;
515
516	iaddr.s_addr = inet_addr("17.202.101.100");
517	if ([macNCOptions encodeAFPPath
518	     : iaddr
519	     : 0x1234
520	     : "volumeName"
521	     : AFP_DIRID_NULL
522	     : AFP_PATHTYPE_LONG
523	     : "this:is:the:path"
524	     : ':'
525	     Into: (void *)buf
526	     Length: &l
527	     ErrorString: [options errString]] == FALSE) {
528	    printf("conversion path failed %s\n", [options errString]);
529	}
530	else {
531	    printf("conversion OK\n");
532	    [options printType:macNCtype_afp_path_e Size:0 Option:(void *)buf
533	     Length:l];
534	    printf("\n");
535	}
536
537    }
538    [options free];
539    options = nil;
540    {
541	unsigned char buf[300];
542	int len = sizeof(buf);
543	id o = [[macNCOptions alloc] initWithBuffer:(void *)buf Size:len];
544
545	if (o == nil) {
546	    printf("initWithBuffer failed\n");
547	}
548	else {
549	    if ([o addOption:macNCtag_user_name_e FromString:"Mac NC # 22"]
550		== FALSE
551		||
552		[o addOption:dhcptag_subnet_mask_e FromString:"255.255.255.0"]
553		== FALSE
554		||
555		[o addOption:dhcptag_end_e Length:0 Data:0] == FALSE
556		) {
557		printf("%s", [o errString]);
558	    }
559	    else {
560		[o parse];
561		[o print];
562	    }
563	}
564    }
565
566#endif
567
568    exit(0);
569}
570#endif /* TEST_MACNC_OPTIONS */
571
572