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