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 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(&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 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