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