xref: /illumos-gate/usr/src/lib/print/libipp-core/common/read.c (revision 168c213023b7f347f11abfc72f448b0c621ab718)
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 2006 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 
47 static papi_status_t
48 read_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 
96 static 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 */
110 static int
111 validate_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 */
122 static int8_t
123 base_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 */
149 static papi_status_t
150 validate_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 */
167 static int
168 validate_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 
269 static papi_status_t
270 ipp_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 
587 static papi_status_t
588 ipp_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(&s, &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 
634 static papi_status_t
635 ipp_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 
652 papi_status_t
653 ipp_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