/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * */ /* $Id: read.c 146 2006-03-24 00:26:54Z njacobs $ */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #define _ipp_tag_string(id) ipp_tag_string((id), buf, sizeof (buf)) static papi_status_t read_name_with_language(ipp_reader_t iread, void *fd, papi_attribute_t ***message) { char *string; uint16_t size; /* read the language */ if (iread(fd, &size, 2) != 2) { ipp_set_status(message, PAPI_BAD_REQUEST, "read failed: lang len\n"); return (PAPI_BAD_REQUEST); } size = (uint16_t)ntohs(size); if ((string = alloca(size + 1)) == NULL) { ipp_set_status(message, PAPI_TEMPORARY_ERROR, "Memory allocation failed"); return (PAPI_TEMPORARY_ERROR); } if (iread(fd, string, size) != size) { ipp_set_status(message, PAPI_BAD_REQUEST, "read failed: lang\n"); return (PAPI_BAD_REQUEST); } /* read the text */ if (iread(fd, &size, 2) != 2) { ipp_set_status(message, PAPI_BAD_REQUEST, "read failed: text len\n"); return (PAPI_BAD_REQUEST); } size = (uint16_t)ntohs(size); if ((string = alloca(size + 1)) == NULL) { ipp_set_status(message, PAPI_TEMPORARY_ERROR, "Memory allocation failed"); return (PAPI_TEMPORARY_ERROR); } if (iread(fd, string, size) != size) { ipp_set_status(message, PAPI_BAD_REQUEST, "read failed: text\n"); return (PAPI_BAD_REQUEST); } return (PAPI_OK); } static struct { int8_t ipp_type; int8_t size; } type_info[] = { { VTAG_INTEGER, 4 }, { VTAG_ENUM, 4 }, { VTAG_BOOLEAN, 1 }, { VTAG_RANGE_OF_INTEGER, 8 }, { VTAG_RESOLUTION, 9 }, { VTAG_DATE_TIME, 11 }, { DTAG_MIN, 0 } }; /* verify that the IPP type and size are compatible */ static int validate_length(int8_t type, int8_t size) { int i; for (i = 0; type_info[i].ipp_type != DTAG_MIN; i++) if (type_info[i].ipp_type == type) return ((type_info[i].size == size) ? 0 : -1); return (0); } /* convert tyep IPP type to a type that is marginally compatible */ static int8_t base_type(int8_t i) { switch (i) { case VTAG_ENUM: case VTAG_INTEGER: return (VTAG_INTEGER); case VTAG_URI: case VTAG_OCTET_STRING: case VTAG_TEXT_WITHOUT_LANGUAGE: case VTAG_URI_SCHEME: case VTAG_CHARSET: case VTAG_NATURAL_LANGUAGE: case VTAG_MIME_MEDIA_TYPE: case VTAG_NAME_WITHOUT_LANGUAGE: case VTAG_KEYWORD: return (VTAG_TEXT_WITHOUT_LANGUAGE); case VTAG_BOOLEAN: case VTAG_RANGE_OF_INTEGER: case VTAG_DATE_TIME: case VTAG_RESOLUTION: default: return (i); } } /* verify that the IPP type is correct for the named attribute */ static papi_status_t validate_type(char *name, int8_t type) { int8_t t = name_to_ipp_type(name); if (t == 0) /* The attribute is not defined in the RFC */ return (PAPI_NOT_FOUND); else if (t == type) /* The supplied type matched the RFC type */ return (PAPI_OK); else { /* The supplied type doesn't match the RFC */ if (base_type(t) == base_type(type)) return (PAPI_OK); return (PAPI_CONFLICT); } } /* verify that the IPP value is within specification for the named attribute */ static int validate_value(papi_attribute_t ***message, char *name, int8_t type, ...) { #define within(a, b, c) ((b >= a) && (b <= c)) va_list ap; int rc = -1; int min = min_val_len(type, name), max = max_val_len(type, name); char buf[64]; /* For _ipp_<...>_string() */ va_start(ap, type); switch (type) { case VTAG_ENUM: case VTAG_INTEGER: { int32_t i = (int32_t)va_arg(ap, int32_t); if (within(min, i, max)) rc = 0; else ipp_set_status(message, PAPI_BAD_ARGUMENT, "%s(%s): %d: out of range (%d - %d)", name, _ipp_tag_string(type), i, min, max); } break; case VTAG_BOOLEAN: { int8_t v = (int8_t)va_arg(ap, int); if (within(0, v, 1)) rc = 0; else ipp_set_status(message, PAPI_BAD_ARGUMENT, "%s(%s): %d: out of range (0 - 1)", name, _ipp_tag_string(type), v); } break; case VTAG_RANGE_OF_INTEGER: { int32_t lower = (int32_t)va_arg(ap, int32_t); int32_t upper = (int32_t)va_arg(ap, int32_t); if (within(min, lower, max) && within(min, upper, max)) rc = 0; else ipp_set_status(message, PAPI_BAD_ARGUMENT, "%s(%s): %d - %d: out of range (%d - %d)", name, _ipp_tag_string(type), lower, upper, min, max); } break; case VTAG_URI: case VTAG_OCTET_STRING: case VTAG_TEXT_WITHOUT_LANGUAGE: case VTAG_URI_SCHEME: case VTAG_CHARSET: case VTAG_NATURAL_LANGUAGE: case VTAG_MIME_MEDIA_TYPE: case VTAG_NAME_WITHOUT_LANGUAGE: { char *v = (char *)va_arg(ap, char *); if (strlen(v) < max) rc = 0; else ipp_set_status(message, PAPI_BAD_ARGUMENT, "%s(%s): %s: too long (max length: %d)", name, _ipp_tag_string(type), v, max); } break; case VTAG_KEYWORD: { char *v = (char *)va_arg(ap, char *); if (strlen(v) >= max) ipp_set_status(message, PAPI_BAD_ARGUMENT, "%s(%s): %s: too long (max length: %d)", name, _ipp_tag_string(type), v, max); else if (is_keyword(v) == 0) ipp_set_status(message, PAPI_BAD_ARGUMENT, "%s(%s): %s: invalid keyword", name, _ipp_tag_string(type), v); else rc = 0; } break; case VTAG_DATE_TIME: case VTAG_RESOLUTION: default: rc = 0; } va_end(ap); return (rc); #undef within } /* * read_attr_group() reads in enough of the message data to parse an entire * attribute group. Since to determine that the group is finished you have to * read the character that determines the type of the next group, this function * must return that character, in order that our caller knows how to call us for * the next group. Thus type is used both as an input parameter (the type of * attribute group to read in) and an output parameter (the type of the next * attribute group). */ static papi_status_t ipp_read_attribute_group(ipp_reader_t iread, void *fd, int8_t *type, papi_attribute_t ***message) { int8_t value_tag; uint16_t name_length, value_length; papi_attribute_t **attributes = NULL; char *name = NULL; int i; char buf[64]; /* For _ipp_<...>_string() */ /* * RFC2910 3.3 says we need to handle `An expected but missing * "begin-attribute-group-tag" field. How? */ if (*type > DTAG_MAX) { /* Scream bloody murder, or assign a new type? */ ipp_set_status(message, PAPI_BAD_REQUEST, "Bad attribute group tag 0x%.2hx (%s)", *type, _ipp_tag_string(*type)); return (PAPI_BAD_REQUEST); } /* This loops through *values* not *attributes*! */ for (i = 0; ; i++) { papi_status_t valid = PAPI_OK; if (iread(fd, &value_tag, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: value tag\n"); return (PAPI_BAD_REQUEST); } /* are we done with this group ? */ if (value_tag <= DTAG_MAX) break; if (iread(fd, &name_length, 2) != 2) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: name length\n"); return (PAPI_BAD_REQUEST); } name_length = (uint16_t)ntohs(name_length); /* Not just another value for the previous attribute */ if (name_length != 0) { if ((name = alloca(name_length + 1)) == NULL) { ipp_set_status(message, PAPI_TEMPORARY_ERROR, "alloca(): failed\n"); return (PAPI_TEMPORARY_ERROR); } (void) memset(name, 0, name_length + 1); if (iread(fd, name, name_length) != name_length) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: name\n"); return (PAPI_BAD_REQUEST); } } valid = validate_type(name, value_tag); if ((valid != PAPI_OK) && (valid != PAPI_NOT_FOUND)) ipp_set_status(message, valid, "%s(%s): %s", name, _ipp_tag_string(value_tag), papiStatusString(valid)); if (iread(fd, &value_length, 2) != 2) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: value length\n"); return (PAPI_BAD_REQUEST); } value_length = (uint16_t)ntohs(value_length); if (validate_length(value_tag, value_length) < 0) { ipp_set_status(message, PAPI_BAD_REQUEST, "Bad value length (%d) for type %s", value_length, _ipp_tag_string(value_tag)); return (PAPI_BAD_REQUEST); } switch (value_tag) { case VTAG_INTEGER: case VTAG_ENUM: { int32_t v; if (iread(fd, &v, value_length) != value_length) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: int/enum\n"); return (PAPI_BAD_REQUEST); } v = (int32_t)ntohl(v); (void) validate_value(message, name, value_tag, v); papiAttributeListAddInteger(&attributes, PAPI_ATTR_APPEND, name, v); } break; case VTAG_BOOLEAN: { int8_t v; if (iread(fd, &v, value_length) != value_length) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: boolean\n"); return (PAPI_BAD_REQUEST); } (void) validate_value(message, name, value_tag, v); papiAttributeListAddBoolean(&attributes, PAPI_ATTR_APPEND, name, v); } break; case VTAG_RANGE_OF_INTEGER: { int32_t min, max; if (iread(fd, &min, 4) != 4) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: min\n"); return (PAPI_BAD_REQUEST); } if (iread(fd, &max, 4) != 4) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: max\n"); return (PAPI_BAD_REQUEST); } min = (int32_t)ntohl(min); max = (int32_t)ntohl(max); (void) validate_value(message, name, value_tag, min, max); papiAttributeListAddRange(&attributes, PAPI_ATTR_APPEND, name, min, max); } break; case VTAG_RESOLUTION: { int32_t x, y; int8_t units; if (iread(fd, &x, 4) != 4) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: x\n"); return (PAPI_BAD_REQUEST); } if (iread(fd, &y, 4) != 4) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: y\n"); return (PAPI_BAD_REQUEST); } if (iread(fd, &units, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: units\n"); return (PAPI_BAD_REQUEST); } x = (int32_t)ntohl(x); y = (int32_t)ntohl(y); papiAttributeListAddResolution(&attributes, PAPI_ATTR_APPEND, name, x, y, (papi_resolution_unit_t)units); } break; case VTAG_DATE_TIME: { struct tm tm; time_t v; int8_t c; uint16_t s; (void) memset(&tm, 0, sizeof (tm)); if (iread(fd, &s, 2) != 2) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: year\n"); return (PAPI_BAD_REQUEST); } tm.tm_year = (uint16_t)ntohs(s) - 1900; if (iread(fd, &c, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: month\n"); return (PAPI_BAD_REQUEST); } tm.tm_mon = c - 1; if (iread(fd, &c, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: day\n"); return (PAPI_BAD_REQUEST); } tm.tm_mday = c; if (iread(fd, &c, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: hour\n"); return (PAPI_BAD_REQUEST); } tm.tm_hour = c; if (iread(fd, &c, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: minutes\n"); return (PAPI_BAD_REQUEST); } tm.tm_min = c; if (iread(fd, &c, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: seconds\n"); return (PAPI_BAD_REQUEST); } tm.tm_sec = c; if (iread(fd, &c, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: decisec\n"); return (PAPI_BAD_REQUEST); } /* tm.deciseconds = c; */ if (iread(fd, &c, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: utc_dir\n"); return (PAPI_BAD_REQUEST); } /* tm.utc_dir = c; */ if (iread(fd, &c, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: utc_hour\n"); return (PAPI_BAD_REQUEST); } /* tm.utc_hours = c; */ if (iread(fd, &c, 1) != 1) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: utc_min\n"); return (PAPI_BAD_REQUEST); } /* tm.utc_minutes = c; */ v = mktime(&tm); (void) validate_value(message, name, value_tag, v); papiAttributeListAddDatetime(&attributes, PAPI_ATTR_APPEND, name, v); } break; case VTAG_NAME_WITH_LANGUAGE: case VTAG_TEXT_WITH_LANGUAGE: /* * we are dropping this because we don't support * name with language at this time. */ (void) read_name_with_language(iread, fd, message); break; case VTAG_NAME_WITHOUT_LANGUAGE: case VTAG_TEXT_WITHOUT_LANGUAGE: case VTAG_URI: case VTAG_KEYWORD: case VTAG_CHARSET: { char *v; if ((v = calloc(1, value_length + 1)) == NULL) { ipp_set_status(message, PAPI_TEMPORARY_ERROR, "calloc(): failed\n"); return (PAPI_TEMPORARY_ERROR); } #ifdef NOTDEF if (iread(fd, v, value_length) != value_length) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: stringy\n"); return (PAPI_BAD_REQUEST); } #else { int rc, i = value_length; char *p = v; while ((rc = iread(fd, p, i)) != i) { if (rc <= 0) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: stringy\n"); return (PAPI_BAD_REQUEST); } i -= rc; p += rc; } } #endif (void) validate_value(message, name, value_tag, v); papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, name, v); } break; case VTAG_UNKNOWN: case VTAG_NOVALUE: case VTAG_UNSUPPORTED: papiAttributeListAddValue(&attributes, PAPI_ATTR_EXCL, name, PAPI_COLLECTION, NULL); break; default: { char *v; if ((v = calloc(1, value_length + 1)) == NULL) { ipp_set_status(message, PAPI_TEMPORARY_ERROR, "calloc(): failed\n"); return (PAPI_TEMPORARY_ERROR); } if (iread(fd, v, value_length) != value_length) { ipp_set_status(message, PAPI_BAD_REQUEST, "bad read: other\n"); return (PAPI_BAD_REQUEST); } papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, name, v); } break; } } if (attributes != NULL) { char name[32]; (void) ipp_tag_string(*type, name, sizeof (name)); papiAttributeListAddCollection(message, PAPI_ATTR_APPEND, name, attributes); } *type = value_tag; return (PAPI_OK); } static papi_status_t ipp_read_header(ipp_reader_t iread, void *fd, papi_attribute_t ***message, char type) { char *attr_name = "status-code"; /* default to a response */ char buf[8]; int8_t c; uint16_t s; int32_t i; if ((iread == NULL) || (fd == NULL) || (message == NULL)) return (PAPI_BAD_ARGUMENT); /* * Apache 1.X uses the buffer supplied to it's read call to read in * the chunk size when chunking is used. This causes problems * reading the header a piece at a time, because we don't have * enough room to read in the chunk size prior to reading the * chunk. */ if (iread(fd, buf, 8) != 8) return (PAPI_BAD_REQUEST); c = buf[0]; (void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE, "version-major", c); c = buf[1]; (void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE, "version-minor", c); memcpy(&s, &buf[2], 2); s = (uint16_t)ntohs(s); if (type == IPP_TYPE_REQUEST) attr_name = "operation-id"; (void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE, attr_name, s); memcpy(&i, &buf[4], 4); i = (uint32_t)ntohl(i); (void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE, "request-id", i); return (PAPI_OK); } static papi_status_t ipp_read_attribute_groups(ipp_reader_t iread, void *fd, papi_attribute_t ***message) { papi_status_t result = PAPI_OK; int8_t tag; /* start reading the attribute groups */ if (iread(fd, &tag, 1) != 1) /* prime the pump */ return (PAPI_BAD_REQUEST); while ((tag != DTAG_END_OF_ATTRIBUTES) && (result == PAPI_OK)) { result = ipp_read_attribute_group(iread, fd, &tag, message); } return (result); } papi_status_t ipp_read_message(ipp_reader_t iread, void *fd, papi_attribute_t ***message, char type) { papi_status_t result = PAPI_OK; if ((iread == NULL) || (fd == NULL) || (message == NULL)) return (PAPI_BAD_ARGUMENT); result = ipp_read_header(iread, fd, message, type); if (result == PAPI_OK) result = ipp_read_attribute_groups(iread, fd, message); return (result); }