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 = (int)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 = (int)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      default:
174	return (dhcptype_from_str(str, type, buf, len_p, err));
175    }
176    return (TRUE);
177}
178
179static void
180S_replace_separators(u_char * buf, int len, u_char sep, u_char new_sep)
181{
182    int i;
183
184    for (i = 0; i < len; i++) {
185	if (buf[i] == sep)
186	    buf[i] = new_sep;
187    }
188    return;
189}
190
191boolean_t
192macNCopt_encodeAFPPath(struct in_addr iaddr, uint16_t port,
193		       const char * volname, uint32_t dirID,
194		       uint8_t pathtype, const char * pathname,
195		       char separator, void * buf,
196		       int * len_p, dhcpo_err_str_t * err)
197{
198    void * 	buf_p = buf;
199    int 	l;
200
201    l = (int)strlen(volname) + (int)strlen(pathname);
202    if (l > AFP_PATH_LIMIT) {
203	if (err)
204	    snprintf(err->str, sizeof(err->str),
205		     "volume/path name length %d > %d-byte limit", l,
206		     AFP_PATH_LIMIT);
207	return (FALSE);
208    }
209
210    if ((l + AFP_PATH_OVERHEAD) > *len_p) {
211	if (err)
212	    snprintf(err->str, sizeof(err->str),
213		     "buffer too small: %d > %d", l + AFP_PATH_OVERHEAD,
214		    *len_p);
215	return (FALSE);
216    }
217    *len_p = l + AFP_PATH_OVERHEAD;			/* option len */
218
219    *((struct in_addr *)buf_p) = iaddr;		/* ip */
220    buf_p += sizeof(iaddr);
221
222    *((u_short *)buf_p) = port;			/* port */
223    buf_p += sizeof(port);
224
225    l = (int)strlen(volname);			/* VolName */
226    *((u_char *)buf_p) = l;
227    buf_p++;
228    if (l)
229	bcopy(volname, (u_char *)buf_p, l);
230    buf_p += l;
231
232    *((uint32_t *)buf_p) = dirID;			/* DirID */
233    buf_p += sizeof(dirID);
234
235    *((uint8_t *)buf_p) = pathtype;		/* AFPPathType */
236    buf_p += sizeof(pathtype);
237
238    l = (int)strlen(pathname);			/* PathName */
239    *((uint8_t *)buf_p) = l;
240    buf_p++;
241    if (l) {
242	bcopy(pathname, (u_char *)buf_p, l);
243	S_replace_separators(buf_p, l, separator, AFP_PATH_SEPARATOR);
244    }
245
246    return (TRUE);
247}
248
249static void
250print_pstring(const uint8_t * option)
251
252{
253    int 	i;
254    int		len = option[0];
255
256    for (i = 0; i < len; i++) {
257	char ch = option[1 + i];
258	printf("%c", ch ? ch : '.');
259    }
260}
261
262static void
263macNC_print_type(dhcptype_t type, void * opt, int option_len)
264{
265    int 	offset;
266    uint8_t * 	option = opt;
267
268    switch (type) {
269      case macNCtype_afp_password_e:
270	if (option_len != AFP_PASSWORD_LEN)
271	    printf("bad password field\n");
272	else {
273	    char buf[9];
274	    strncpy(buf, (char *)opt, AFP_PASSWORD_LEN);
275	    buf[8] = '\0';
276	    printf("%s", buf);
277	}
278	break;
279      case macNCtype_afp_path_e:
280	offset = 0;
281	printf("(");
282
283	dhcptype_print(dhcptype_ip_e, option, option_len);
284	offset += 4;
285
286	printf(", ");
287	dhcptype_print(dhcptype_uint16_e, option + offset, option_len);
288	offset += 2;
289
290	printf(", ");
291	print_pstring(option + offset);
292	offset += option[offset] + 1;
293
294	printf(", ");
295	dhcptype_print(dhcptype_uint32_e, option + offset, option_len);
296	offset += 4;
297
298	printf(", ");
299	dhcptype_print(dhcptype_uint8_e, option + offset, option_len);
300	offset += 1;
301
302	printf(", ");
303	print_pstring(option + offset);
304	printf(")");
305	break;
306
307      case macNCtype_pstring_e: {
308	print_pstring(option);
309	break;
310      }
311      default:
312	dhcptype_print(type, option, option_len);
313	break;
314    }
315    return;
316}
317
318boolean_t
319macNC_print_option(void * vopt)
320{
321    u_char *    		opt = vopt;
322    u_char 			tag = opt[TAG_OFFSET];
323    u_char 			option_len = opt[LEN_OFFSET];
324    u_char * 			option = opt + OPTION_OFFSET;
325    const dhcptag_info_t * 	entry;
326
327    entry = macNCtag_info(tag);
328    if (entry == NULL)
329	return (FALSE);
330    {
331	const dhcptype_info_t * type = macNCtype_info(entry->type);
332
333	if (type == NULL) {
334	    printf("unknown type %d\n", entry->type);
335	    return (FALSE);
336	}
337	printf("%s (%s): ", entry->name, type->name);
338	if (tag == dhcptag_dhcp_message_type_e)
339	    printf("%s ", dhcp_msgtype_names(*option));
340	macNC_print_type(entry->type, option, option_len);
341	printf("\n");
342    }
343    return (TRUE);
344}
345
346void
347macNCopt_print(dhcpol_t * list)
348{
349    int 		i;
350
351    printf("Options count is %d\n", dhcpol_count(list));
352    for (i = 0; i < dhcpol_count(list); i++) {
353	unsigned char * option = dhcpol_element(list, i);
354	if (macNC_print_option(option) == FALSE)
355	    printf("undefined tag %d len %d\n", option[TAG_OFFSET],
356		   option[LEN_OFFSET]);
357    }
358}
359
360#ifdef TEST_MACNC_OPTIONS
361
362/**
363 **
364 ** Testing 1 2 3
365 **
366 **/
367u_char test[] =
368{
369    dhcptag_subnet_mask_e,
370    4,
371    255, 255, 252, 0,
372
373    dhcptag_router_e,
374    12,
375    17, 202, 40, 1,
376    17, 202, 41, 1,
377    17, 202, 42, 1,
378
379    dhcptag_domain_name_server_e,
380    4,
381    17, 128, 100, 12,
382
383    dhcptag_host_name_e,
384    7,
385    's', 'i', 'e', 'g', 'd', 'i', '7',
386
387    dhcptag_pad_e,
388
389    dhcptag_all_subnets_local_e,
390    1,
391    0,
392
393    dhcptag_vendor_specific_e,
394    24,
395    't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 't', 'e', 's', 't',
396    234, 212, 0, 1, 2, 3, 4, 5, 6, 7,
397
398    macNCtag_user_name_e,
399    10,
400    'M', 'a', 'c', 'N', 'C', ' ', '#', ' ', '1', '9',
401
402    macNCtag_shared_system_file_e,
403    29,
404    17, 202, 40, 191,
405    0x20, 0x00,
406    4, 'a', 'b', 'c', 'd',
407    0, 0, 0, 0,
408    2,
409    12, 0, 'e', 0, 'f', 0, 'g', 'h', 'i', 0, 'j', 'k', 'l',
410
411    dhcptag_end_e,
412};
413
414#if 0
415u_char test[] = {
4160x01, 0x04, 0xff, 0x00, 0x00, 0x00, 0x03, 0x04, 0x0f, 0x03, 0x03, 0x09, 0x06,
4170x04, 0x0f, 0x03, 0x03, 0x09, 0xe8, 0x09, 0x08, 0x6d, 0x61, 0x63, 0x6e, 0x63, 0x30, 0x30, 0x30,
4180xed, 0x09, 0x08, 0x6d, 0x61, 0x63, 0x6e, 0x63, 0x30, 0x30, 0x30, 0xe9, 0x09, 0x08, 0x74, 0x65,
4190x73, 0x74, 0x69, 0x6e, 0x67, 0x32, 0xea, 0x18, 0x0f, 0x03, 0x03, 0x05, 0x02, 0x24, 0x00, 0x06,
4200x64, 0x75, 0x63, 0x61, 0x74, 0x73, 0x09, 0x31, 0x35, 0x2e, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x37,
4210xeb, 0x18, 0x0f, 0x03, 0x03, 0x05, 0x02, 0x24, 0x00, 0x06, 0x64, 0x75, 0x63, 0x61, 0x74, 0x73,
4220x09, 0x31, 0x35, 0x2e, 0x33, 0x2e, 0x33, 0x2e, 0x31, 0x37, 0xec, 0x18, 0x0f, 0x03, 0x03, 0x05,
4230x02, 0x24, 0x00, 0x06, 0x64, 0x75, 0x63, 0x61, 0x74, 0x73, 0x09, 0x31, 0x35, 0x2e, 0x33, 0x2e,
4240x33, 0x2e, 0x31, 0x37, 0xff,
425};
426#endif
427
428int
429main()
430{
431    dhcpo_err_str_t 	err;
432    dhcpol_t 		list;
433
434    dhcpol_init(&list);
435    if (dhcpol_parse_buffer(&list, test, sizeof(test), &err) == FALSE) {
436	printf("parse test failed, %s\n", err.str);
437	exit(1);
438    }
439    macNCopt_print(&list);
440    exit(0);
441#if 0
442    {
443	struct in_addr	iaddr;
444	int i = sizeof(iaddr);
445
446	if ([options str:"17.202.42.129" ToType:dhcptype_ip_e
447	     Buffer:(void *)&iaddr Length:&i] == FALSE) {
448	    printf("conversion failed %s\n", [options errString]);
449	}
450	else {
451	    printf("ip address should be 17.202.42.129: ");
452	    [options printType:dhcptype_ip_e Size:i Option:(void *)&iaddr
453	     Length:i];
454	    printf("\n");
455	}
456    }
457    {
458	unsigned char buf[32] = "Mac NC #33";
459	unsigned char buf2[34];
460	int len = sizeof(buf2);
461
462	if ([options str:buf ToType:macNCtype_pstring_e Buffer:buf2
463	     Length:&len] == FALSE) {
464	    printf("conversion failed %s\n", [options errString]);
465	}
466	else {
467	    printf("macNCtype string should be %s:", buf);
468	    [options printType:macNCtype_pstring_e Size:0 Option:buf2
469	     Length:len];
470	    printf("\n");
471	}
472    }
473    {
474	struct in_addr	iaddr[10];
475	int l = sizeof(iaddr);
476	u_char * strList[] = { "17.202.40.1", "17.202.41.1", "17.202.42.1",
477			     "17.202.43.1" };
478	int num = sizeof(strList) / sizeof(*strList);
479
480	if ([options strList:strList Number:num Tag:dhcptag_router_e
481	     Buffer:(void *)iaddr Length:&l] == FALSE) {
482	    printf("conversion failed %s\n", [options errString]);
483	}
484	else {
485	    [options printType:dhcptype_ip_mult_e Size:4 Option:(void *)iaddr
486	     Length:l];
487	    printf("\n");
488	}
489    }
490    {
491	u_char buf[100];
492	u_char * strList[] = { "17.86.91.2", "0x100", "0", "greatVolumeName",
493				   "/spectacular/path/name/eh" };
494	int l = sizeof(buf);
495	int num = sizeof(strList) / sizeof(*strList);
496
497	if ([macNCOptions strList:strList Number:num
498	     Tag:macNCtag_system_path_shared_e Buffer:(void *)buf Length:&l
499	     ErrorString:[options errString]] == FALSE) {
500	    printf("conversion failed %s\n", [options errString]);
501	}
502	else {
503	    printf("conversion OK\n");
504	    [options printType:macNCtype_afp_path_e Size:0 Option:(void *)buf
505	     Length:l];
506	    printf("\n");
507	}
508    }
509    {
510	u_char buf[100];
511	int l = sizeof(buf);
512	struct in_addr iaddr;
513
514	iaddr.s_addr = inet_addr("17.202.101.100");
515	if ([macNCOptions encodeAFPPath
516	     : iaddr
517	     : 0x1234
518	     : "volumeName"
519	     : AFP_DIRID_NULL
520	     : AFP_PATHTYPE_LONG
521	     : "this:is:the:path"
522	     : ':'
523	     Into: (void *)buf
524	     Length: &l
525	     ErrorString: [options errString]] == FALSE) {
526	    printf("conversion path failed %s\n", [options errString]);
527	}
528	else {
529	    printf("conversion OK\n");
530	    [options printType:macNCtype_afp_path_e Size:0 Option:(void *)buf
531	     Length:l];
532	    printf("\n");
533	}
534
535    }
536    [options free];
537    options = nil;
538    {
539	unsigned char buf[300];
540	int len = sizeof(buf);
541	id o = [[macNCOptions alloc] initWithBuffer:(void *)buf Size:len];
542
543	if (o == nil) {
544	    printf("initWithBuffer failed\n");
545	}
546	else {
547	    if ([o addOption:macNCtag_user_name_e FromString:"Mac NC # 22"]
548		== FALSE
549		||
550		[o addOption:dhcptag_subnet_mask_e FromString:"255.255.255.0"]
551		== FALSE
552		||
553		[o addOption:dhcptag_end_e Length:0 Data:0] == FALSE
554		) {
555		printf("%s", [o errString]);
556	    }
557	    else {
558		[o parse];
559		[o print];
560	    }
561	}
562    }
563
564#endif
565
566    exit(0);
567}
568#endif /* TEST_MACNC_OPTIONS */
569
570