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