read.c revision 4030:8649b543e698
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 *
26 */
27
28/* $Id: read.c 146 2006-03-24 00:26:54Z njacobs $ */
29
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <alloca.h>
35#include <string.h>
36#include <stdarg.h>
37#include <sys/types.h>
38#include <netinet/in.h>
39#include <inttypes.h>
40
41#include <papi.h>
42#include <ipp.h>
43
44
45#define	_ipp_tag_string(id) ipp_tag_string((id), buf, sizeof (buf))
46
47static papi_status_t
48read_name_with_language(ipp_reader_t iread, void *fd,
49			papi_attribute_t ***message)
50{
51	char *string;
52	uint16_t size;
53
54	/* read the language */
55	if (iread(fd, &size, 2) != 2) {
56		ipp_set_status(message, PAPI_BAD_REQUEST,
57				"read failed: lang len\n");
58		return (PAPI_BAD_REQUEST);
59	}
60	size = (uint16_t)ntohs(size);
61
62	if ((string = alloca(size + 1)) == NULL) {
63		ipp_set_status(message, PAPI_TEMPORARY_ERROR,
64				"Memory allocation failed");
65		return (PAPI_TEMPORARY_ERROR);
66	}
67	if (iread(fd, string, size) != size) {
68		ipp_set_status(message, PAPI_BAD_REQUEST,
69				"read failed: lang\n");
70		return (PAPI_BAD_REQUEST);
71	}
72
73	/* read the text */
74	if (iread(fd, &size, 2) != 2) {
75		ipp_set_status(message, PAPI_BAD_REQUEST,
76				"read failed: text len\n");
77		return (PAPI_BAD_REQUEST);
78	}
79	size = (uint16_t)ntohs(size);
80
81	if ((string = alloca(size + 1)) == NULL) {
82		ipp_set_status(message, PAPI_TEMPORARY_ERROR,
83				"Memory allocation failed");
84		return (PAPI_TEMPORARY_ERROR);
85	}
86	if (iread(fd, string, size) != size) {
87		ipp_set_status(message, PAPI_BAD_REQUEST,
88				"read failed: text\n");
89		return (PAPI_BAD_REQUEST);
90	}
91
92	return (PAPI_OK);
93}
94
95
96static struct {
97	int8_t	ipp_type;
98	int8_t	size;
99} type_info[] = {
100	{ VTAG_INTEGER,			4 },
101	{ VTAG_ENUM,			4 },
102	{ VTAG_BOOLEAN,			1 },
103	{ VTAG_RANGE_OF_INTEGER,	8 },
104	{ VTAG_RESOLUTION,		9 },
105	{ VTAG_DATE_TIME,		11 },
106	{ DTAG_MIN,			0 }
107};
108
109/* verify that the IPP type and size are compatible */
110static int
111validate_length(int8_t type, int8_t size)
112{
113	int i;
114
115	for (i = 0; type_info[i].ipp_type != DTAG_MIN; i++)
116		if (type_info[i].ipp_type == type)
117			return ((type_info[i].size == size) ? 0 : -1);
118	return (0);
119}
120
121/* convert tyep IPP type to a type that is marginally compatible */
122static int8_t
123base_type(int8_t i)
124{
125	switch (i) {
126	case VTAG_ENUM:
127	case VTAG_INTEGER:
128		return (VTAG_INTEGER);
129	case VTAG_URI:
130	case VTAG_OCTET_STRING:
131	case VTAG_TEXT_WITHOUT_LANGUAGE:
132	case VTAG_URI_SCHEME:
133	case VTAG_CHARSET:
134	case VTAG_NATURAL_LANGUAGE:
135	case VTAG_MIME_MEDIA_TYPE:
136	case VTAG_NAME_WITHOUT_LANGUAGE:
137	case VTAG_KEYWORD:
138		return (VTAG_TEXT_WITHOUT_LANGUAGE);
139	case VTAG_BOOLEAN:
140	case VTAG_RANGE_OF_INTEGER:
141	case VTAG_DATE_TIME:
142	case VTAG_RESOLUTION:
143	default:
144		return (i);
145	}
146}
147
148/* verify that the IPP type is correct for the named attribute */
149static papi_status_t
150validate_type(char *name, int8_t type)
151{
152	int8_t t = name_to_ipp_type(name);
153
154	if (t == 0)		/* The attribute is not defined in the RFC */
155		return (PAPI_NOT_FOUND);
156	else if (t == type)	/* The supplied type matched the RFC type */
157		return (PAPI_OK);
158	else {			/* The supplied type doesn't match the RFC */
159		if (base_type(t) == base_type(type))
160			return (PAPI_OK);
161
162		return (PAPI_CONFLICT);
163	}
164}
165
166/* verify that the IPP value is within specification for the named attribute */
167static int
168validate_value(papi_attribute_t ***message, char *name, int8_t type, ...)
169{
170#define	within(a, b, c)	((b >= a) && (b <= c))
171	va_list ap;
172	int rc = -1;
173	int min = min_val_len(type, name),
174	    max = max_val_len(type, name);
175	char buf[64];	/* For _ipp_<...>_string() */
176
177	va_start(ap, type);
178	switch (type) {
179	case VTAG_ENUM:
180	case VTAG_INTEGER: {
181		int32_t i = (int32_t)va_arg(ap, int32_t);
182
183		if (within(min, i, max))
184			rc = 0;
185		else
186			ipp_set_status(message, PAPI_BAD_ARGUMENT,
187				"%s(%s): %d: out of range (%d - %d)", name,
188				_ipp_tag_string(type), i, min, max);
189		}
190		break;
191	case VTAG_BOOLEAN: {
192		int8_t v = (int8_t)va_arg(ap, int);
193
194		if (within(0, v, 1))
195			rc = 0;
196		else
197			ipp_set_status(message, PAPI_BAD_ARGUMENT,
198				"%s(%s): %d: out of range (0 - 1)", name,
199				_ipp_tag_string(type), v);
200		}
201		break;
202	case VTAG_RANGE_OF_INTEGER: {
203		int32_t lower = (int32_t)va_arg(ap, int32_t);
204		int32_t upper = (int32_t)va_arg(ap, int32_t);
205
206		if (within(min, lower, max) &&
207		    within(min, upper, max))
208			rc = 0;
209		else
210			ipp_set_status(message, PAPI_BAD_ARGUMENT,
211				"%s(%s): %d - %d: out of range (%d - %d)", name,
212				_ipp_tag_string(type), lower, upper, min, max);
213		}
214		break;
215	case VTAG_URI:
216	case VTAG_OCTET_STRING:
217	case VTAG_TEXT_WITHOUT_LANGUAGE:
218	case VTAG_URI_SCHEME:
219	case VTAG_CHARSET:
220	case VTAG_NATURAL_LANGUAGE:
221	case VTAG_MIME_MEDIA_TYPE:
222	case VTAG_NAME_WITHOUT_LANGUAGE: {
223		char *v = (char *)va_arg(ap, char *);
224
225		if (strlen(v) < max)
226			rc = 0;
227		else
228			ipp_set_status(message, PAPI_BAD_ARGUMENT,
229				"%s(%s): %s: too long (max length: %d)", name,
230				_ipp_tag_string(type), v, max);
231		}
232		break;
233	case VTAG_KEYWORD: {
234		char *v = (char *)va_arg(ap, char *);
235
236		if (strlen(v) >= max)
237			ipp_set_status(message, PAPI_BAD_ARGUMENT,
238				"%s(%s): %s: too long (max length: %d)", name,
239				_ipp_tag_string(type), v, max);
240		else if (is_keyword(v) == 0)
241			ipp_set_status(message, PAPI_BAD_ARGUMENT,
242				"%s(%s): %s: invalid keyword", name,
243				_ipp_tag_string(type), v);
244		else
245			rc = 0;
246		}
247		break;
248	case VTAG_DATE_TIME:
249	case VTAG_RESOLUTION:
250	default:
251		rc = 0;
252	}
253	va_end(ap);
254
255	return (rc);
256#undef within
257}
258
259/*
260 * read_attr_group() reads in enough of the message data to parse an entire
261 * attribute group.  Since to determine that the group is finished you have to
262 * read the character that determines the type of the next group, this function
263 * must return that character, in order that our caller knows how to call us for
264 * the next group.  Thus type is used both as an input parameter (the type of
265 * attribute group to read in) and an output parameter (the type of the next
266 * attribute group).
267 */
268
269static papi_status_t
270ipp_read_attribute_group(ipp_reader_t iread, void *fd, int8_t *type,
271			papi_attribute_t ***message)
272{
273	int8_t value_tag;
274	uint16_t name_length, value_length;
275	papi_attribute_t **attributes = NULL;
276	char *name = NULL;
277	int i;
278	char buf[64];	/* For _ipp_<...>_string() */
279
280	/*
281	 * RFC2910 3.3 says we need to handle `An expected but missing
282	 * "begin-attribute-group-tag" field.  How?
283	 */
284	if (*type > DTAG_MAX)  {
285		/* Scream bloody murder, or assign a new type? */
286		ipp_set_status(message, PAPI_BAD_REQUEST,
287			"Bad attribute group tag 0x%.2hx (%s)",
288			*type, _ipp_tag_string(*type));
289		return (PAPI_BAD_REQUEST);
290	}
291
292	/* This loops through *values* not *attributes*! */
293	for (i = 0; ; i++) {
294		papi_status_t valid = PAPI_OK;
295		if (iread(fd, &value_tag, 1) != 1) {
296			ipp_set_status(message, PAPI_BAD_REQUEST,
297				"bad read: value tag\n");
298			return (PAPI_BAD_REQUEST);
299		}
300		/* are we done with this group ? */
301		if (value_tag <= DTAG_MAX)
302			break;
303
304		if (iread(fd, &name_length, 2) != 2) {
305			ipp_set_status(message, PAPI_BAD_REQUEST,
306				"bad read: name length\n");
307			return (PAPI_BAD_REQUEST);
308		}
309		name_length = (uint16_t)ntohs(name_length);
310
311		/* Not just another value for the previous attribute */
312		if (name_length != 0) {
313			if ((name = alloca(name_length + 1)) == NULL) {
314				ipp_set_status(message, PAPI_TEMPORARY_ERROR,
315					"alloca(): failed\n");
316				return (PAPI_TEMPORARY_ERROR);
317			}
318			(void) memset(name, 0, name_length + 1);
319
320			if (iread(fd, name, name_length) != name_length) {
321				ipp_set_status(message, PAPI_BAD_REQUEST,
322					"bad read: name\n");
323				return (PAPI_BAD_REQUEST);
324			}
325		}
326
327		valid = validate_type(name, value_tag);
328		if ((valid != PAPI_OK) && (valid != PAPI_NOT_FOUND))
329			ipp_set_status(message, valid, "%s(%s): %s", name,
330				_ipp_tag_string(value_tag),
331				papiStatusString(valid));
332
333		if (iread(fd, &value_length, 2) != 2) {
334			ipp_set_status(message, PAPI_BAD_REQUEST,
335				"bad read: value length\n");
336			return (PAPI_BAD_REQUEST);
337		}
338		value_length = (uint16_t)ntohs(value_length);
339
340		if (validate_length(value_tag, value_length) < 0) {
341			ipp_set_status(message, PAPI_BAD_REQUEST,
342				"Bad value length (%d) for type %s",
343				value_length, _ipp_tag_string(value_tag));
344			return (PAPI_BAD_REQUEST);
345		}
346
347		switch (value_tag) {
348		case VTAG_INTEGER:
349		case VTAG_ENUM: {
350			int32_t v;
351
352			if (iread(fd, &v, value_length) != value_length) {
353				ipp_set_status(message, PAPI_BAD_REQUEST,
354					"bad read: int/enum\n");
355				return (PAPI_BAD_REQUEST);
356			}
357			v = (int32_t)ntohl(v);
358			(void) validate_value(message, name, value_tag, v);
359			papiAttributeListAddInteger(&attributes,
360						PAPI_ATTR_APPEND, name, v);
361
362			}
363			break;
364		case VTAG_BOOLEAN: {
365			int8_t v;
366
367			if (iread(fd, &v, value_length) != value_length) {
368				ipp_set_status(message, PAPI_BAD_REQUEST,
369					"bad read: boolean\n");
370				return (PAPI_BAD_REQUEST);
371			}
372			(void) validate_value(message, name, value_tag, v);
373			papiAttributeListAddBoolean(&attributes,
374						PAPI_ATTR_APPEND, name, v);
375			}
376			break;
377		case VTAG_RANGE_OF_INTEGER: {
378			int32_t min, max;
379
380			if (iread(fd, &min, 4) != 4) {
381				ipp_set_status(message, PAPI_BAD_REQUEST,
382					"bad read: min\n");
383				return (PAPI_BAD_REQUEST);
384			}
385			if (iread(fd, &max, 4) != 4) {
386				ipp_set_status(message, PAPI_BAD_REQUEST,
387					"bad read: max\n");
388				return (PAPI_BAD_REQUEST);
389			}
390			min = (int32_t)ntohl(min);
391			max = (int32_t)ntohl(max);
392			(void) validate_value(message, name, value_tag,
393					min, max);
394			papiAttributeListAddRange(&attributes, PAPI_ATTR_APPEND,
395						name, min, max);
396			}
397			break;
398		case VTAG_RESOLUTION: {
399			int32_t x, y;
400			int8_t units;
401
402			if (iread(fd, &x, 4) != 4) {
403				ipp_set_status(message, PAPI_BAD_REQUEST,
404					"bad read: x\n");
405				return (PAPI_BAD_REQUEST);
406			}
407			if (iread(fd, &y, 4) != 4) {
408				ipp_set_status(message, PAPI_BAD_REQUEST,
409					"bad read: y\n");
410				return (PAPI_BAD_REQUEST);
411			}
412			if (iread(fd, &units, 1) != 1) {
413				ipp_set_status(message, PAPI_BAD_REQUEST,
414					"bad read: units\n");
415				return (PAPI_BAD_REQUEST);
416			}
417			x = (int32_t)ntohl(x);
418			y = (int32_t)ntohl(y);
419			papiAttributeListAddResolution(&attributes,
420						PAPI_ATTR_APPEND, name, x, y,
421						(papi_resolution_unit_t)units);
422			}
423			break;
424		case VTAG_DATE_TIME: {
425			struct tm tm;
426			time_t v;
427			int8_t c;
428			uint16_t s;
429
430			(void) memset(&tm, 0, sizeof (tm));
431			if (iread(fd, &s, 2) != 2) {
432				ipp_set_status(message, PAPI_BAD_REQUEST,
433					"bad read: year\n");
434				return (PAPI_BAD_REQUEST);
435			}
436			tm.tm_year = (uint16_t)ntohs(s) - 1900;
437			if (iread(fd, &c, 1) != 1) {
438				ipp_set_status(message, PAPI_BAD_REQUEST,
439					"bad read: month\n");
440				return (PAPI_BAD_REQUEST);
441			}
442			tm.tm_mon = c - 1;
443			if (iread(fd, &c, 1) != 1) {
444				ipp_set_status(message, PAPI_BAD_REQUEST,
445					"bad read: day\n");
446				return (PAPI_BAD_REQUEST);
447			}
448			tm.tm_mday = c;
449			if (iread(fd, &c, 1) != 1) {
450				ipp_set_status(message, PAPI_BAD_REQUEST,
451					"bad read: hour\n");
452				return (PAPI_BAD_REQUEST);
453			}
454			tm.tm_hour = c;
455			if (iread(fd, &c, 1) != 1) {
456				ipp_set_status(message, PAPI_BAD_REQUEST,
457					"bad read: minutes\n");
458				return (PAPI_BAD_REQUEST);
459			}
460			tm.tm_min = c;
461			if (iread(fd, &c, 1) != 1) {
462				ipp_set_status(message, PAPI_BAD_REQUEST,
463					"bad read: seconds\n");
464				return (PAPI_BAD_REQUEST);
465			}
466			tm.tm_sec = c;
467			if (iread(fd, &c, 1) != 1) {
468				ipp_set_status(message, PAPI_BAD_REQUEST,
469					"bad read: decisec\n");
470				return (PAPI_BAD_REQUEST);
471			}
472			/* tm.deciseconds = c; */
473			if (iread(fd, &c, 1) != 1) {
474				ipp_set_status(message, PAPI_BAD_REQUEST,
475					"bad read: utc_dir\n");
476				return (PAPI_BAD_REQUEST);
477			}
478			/* tm.utc_dir = c; */
479			if (iread(fd, &c, 1) != 1) {
480				ipp_set_status(message, PAPI_BAD_REQUEST,
481					"bad read: utc_hour\n");
482				return (PAPI_BAD_REQUEST);
483			}
484			/* tm.utc_hours = c; */
485			if (iread(fd, &c, 1) != 1) {
486				ipp_set_status(message, PAPI_BAD_REQUEST,
487					"bad read: utc_min\n");
488				return (PAPI_BAD_REQUEST);
489			}
490			/* tm.utc_minutes = c; */
491
492			v = mktime(&tm);
493
494			(void) validate_value(message, name, value_tag, v);
495			papiAttributeListAddDatetime(&attributes,
496						PAPI_ATTR_APPEND, name, v);
497			}
498			break;
499		case VTAG_NAME_WITH_LANGUAGE:
500		case VTAG_TEXT_WITH_LANGUAGE:
501			/*
502			 * we are dropping this because we don't support
503			 * name with language at this time.
504			 */
505			(void) read_name_with_language(iread, fd, message);
506			break;
507		case VTAG_NAME_WITHOUT_LANGUAGE:
508		case VTAG_TEXT_WITHOUT_LANGUAGE:
509		case VTAG_URI:
510		case VTAG_KEYWORD:
511		case VTAG_CHARSET: {
512			char *v;
513
514			if ((v = calloc(1, value_length + 1)) == NULL) {
515				ipp_set_status(message, PAPI_TEMPORARY_ERROR,
516					"calloc(): failed\n");
517				return (PAPI_TEMPORARY_ERROR);
518			}
519#ifdef NOTDEF
520			if (iread(fd, v, value_length) != value_length) {
521				ipp_set_status(message, PAPI_BAD_REQUEST,
522					"bad read: stringy\n");
523				return (PAPI_BAD_REQUEST);
524			}
525#else
526			{
527			int rc, i = value_length;
528			char *p = v;
529
530			while ((rc = iread(fd, p, i)) != i) {
531				if (rc <= 0) {
532					ipp_set_status(message,
533						PAPI_BAD_REQUEST,
534						"bad read: stringy\n");
535					return (PAPI_BAD_REQUEST);
536				}
537				i -= rc;
538				p += rc;
539			}
540			}
541#endif
542			(void) validate_value(message, name, value_tag, v);
543			papiAttributeListAddString(&attributes,
544						PAPI_ATTR_APPEND, name, v);
545			}
546			break;
547		case VTAG_UNKNOWN:
548		case VTAG_NOVALUE:
549		case VTAG_UNSUPPORTED:
550			papiAttributeListAddValue(&attributes, PAPI_ATTR_EXCL,
551					name, PAPI_COLLECTION, NULL);
552			break;
553		default: {
554			char *v;
555
556			if ((v = calloc(1, value_length + 1)) == NULL) {
557				ipp_set_status(message, PAPI_TEMPORARY_ERROR,
558					"calloc(): failed\n");
559				return (PAPI_TEMPORARY_ERROR);
560			}
561			if (iread(fd, v, value_length) != value_length) {
562				ipp_set_status(message, PAPI_BAD_REQUEST,
563					"bad read: other\n");
564				return (PAPI_BAD_REQUEST);
565			}
566			papiAttributeListAddString(&attributes,
567						PAPI_ATTR_APPEND, name, v);
568			}
569			break;
570		}
571	}
572
573	if (attributes != NULL) {
574		char name[32];
575
576		(void) ipp_tag_string(*type, name, sizeof (name));
577		papiAttributeListAddCollection(message, PAPI_ATTR_APPEND, name,
578					attributes);
579	}
580
581	*type = value_tag;
582
583	return (PAPI_OK);
584}
585
586
587static papi_status_t
588ipp_read_header(ipp_reader_t iread, void *fd, papi_attribute_t ***message,
589		char type)
590{
591	char *attr_name = "status-code";	/* default to a response */
592	char buf[8];
593	int8_t c;
594	uint16_t s;
595	int32_t i;
596
597	if ((iread == NULL) || (fd == NULL) || (message == NULL))
598		return (PAPI_BAD_ARGUMENT);
599
600	/*
601	 * Apache 1.X uses the buffer supplied to it's read call to read in
602	 * the chunk size when chunking is used.  This causes problems
603	 * reading the header a piece at a time, because we don't have
604	 * enough room to read in the chunk size prior to reading the
605	 * chunk.
606	 */
607
608	if (iread(fd, buf, 8) != 8)
609		return (PAPI_BAD_REQUEST);
610
611	c = buf[0];
612	(void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE,
613				"version-major", c);
614
615	c = buf[1];
616	(void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE,
617				"version-minor", c);
618
619	memcpy(&s, &buf[2], 2);
620	s = (uint16_t)ntohs(s);
621	if (type == IPP_TYPE_REQUEST)
622		attr_name = "operation-id";
623	(void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE,
624				attr_name, s);
625
626	memcpy(&i, &buf[4], 4);
627	i = (uint32_t)ntohl(i);
628	(void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE,
629				"request-id", i);
630
631	return (PAPI_OK);
632}
633
634static papi_status_t
635ipp_read_attribute_groups(ipp_reader_t iread, void *fd,
636			papi_attribute_t ***message)
637{
638	papi_status_t result = PAPI_OK;
639	int8_t tag;
640
641	/* start reading the attribute groups */
642	if (iread(fd, &tag, 1) != 1)	/* prime the pump */
643		return (PAPI_BAD_REQUEST);
644
645	while ((tag != DTAG_END_OF_ATTRIBUTES) && (result == PAPI_OK)) {
646		result = ipp_read_attribute_group(iread, fd, &tag, message);
647	}
648
649	return (result);
650}
651
652papi_status_t
653ipp_read_message(ipp_reader_t iread, void *fd, papi_attribute_t ***message,
654		char type)
655{
656	papi_status_t result = PAPI_OK;
657
658	if ((iread == NULL) || (fd == NULL) || (message == NULL))
659		return (PAPI_BAD_ARGUMENT);
660
661	result = ipp_read_header(iread, fd, message, type);
662	if (result == PAPI_OK)
663		result = ipp_read_attribute_groups(iread, fd, message);
664
665	return (result);
666}
667