| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767 |
- /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
- **
- ** Redistribution and use in source and binary forms, with or without
- ** modification, are permitted provided that the following conditions
- ** are met:
- ** 1. Redistributions of source code must retain the above copyright
- ** notice, this list of conditions and the following disclaimer.
- ** 2. Redistributions in binary form must reproduce the above copyright
- ** notice, this list of conditions and the following disclaimer in the
- ** documentation and/or other materials provided with the distribution.
- ** 3. The name of the author may not be used to endorse or promote products
- ** derived from this software without specific prior written permission.
- **
- ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- ** SUCH DAMAGE. */
- #include "xmlrpc_config.h"
- #include <stddef.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <ctype.h>
- #include "xmlrpc.h"
- #include "xmlrpc_int.h"
- #include "xmlrpc_xmlparser.h"
- /*=========================================================================
- ** Data Format
- **=========================================================================
- ** All XML-RPC documents contain a single methodCall or methodResponse
- ** element.
- **
- ** methodCall methodName, params
- ** methodResponse (params|fault)
- ** params param*
- ** param value
- ** fault value
- ** value (i4|int|boolean|string|double|dateTime.iso8601|base64|
- ** struct|array)
- ** array data
- ** data value*
- ** struct member*
- ** member name, value
- **
- ** Contain CDATA: methodName, i4, int, boolean, string, double,
- ** dateTime.iso8601, base64, name
- **
- ** We attempt to validate the structure of the XML document carefully.
- ** We also try *very* hard to handle malicious data gracefully, and without
- ** leaking memory.
- **
- ** The CHECK_NAME and CHECK_CHILD_COUNT macros examine an XML element, and
- ** invoke XMLRPC_FAIL if something looks wrong.
- */
- #define CHECK_NAME(env,elem,name) \
- do \
- if (strcmp((name), xml_element_name(elem)) != 0) \
- XMLRPC_FAIL2(env, XMLRPC_PARSE_ERROR, \
- "Expected element of type <%s>, found <%s>", \
- (name), xml_element_name(elem)); \
- while (0)
- #define CHECK_CHILD_COUNT(env,elem,count) \
- do \
- if (xml_element_children_size(elem) != (count)) \
- XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR, \
- "Expected <%s> to have %d children, found %d", \
- xml_element_name(elem), (count), \
- xml_element_children_size(elem)); \
- while (0)
- static xml_element *
- get_child_by_name (xmlrpc_env *env, xml_element *parent, char *name)
- {
- size_t child_count, i;
- xml_element **children;
- children = xml_element_children(parent);
- child_count = xml_element_children_size(parent);
- for (i = 0; i < child_count; i++) {
- if (0 == strcmp(xml_element_name(children[i]), name))
- return children[i];
- }
-
- xmlrpc_env_set_fault_formatted(env, XMLRPC_PARSE_ERROR,
- "Expected <%s> to have child <%s>",
- xml_element_name(parent), name);
- return NULL;
- }
- /*=========================================================================
- ** Number-Parsing Functions
- **=========================================================================
- ** These functions mirror atoi, atof, etc., but provide better
- ** error-handling. These routines may reset errno to zero.
- */
- static xmlrpc_int32
- xmlrpc_atoi(xmlrpc_env *env, char *str, size_t stringLength,
- xmlrpc_int32 min, xmlrpc_int32 max)
- {
- long i;
- char *end;
- XMLRPC_ASSERT_ENV_OK(env);
- XMLRPC_ASSERT_PTR_OK(str);
- /* Suppress compiler warnings. */
- i = 0;
- /* Check for leading white space. */
- if (isspace((int)(str[0])))
- XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
- "\"%s\" must not contain whitespace", str);
- /* Convert the value. */
- end = str + stringLength;
- errno = 0;
- i = strtol(str, &end, 10);
- /* Look for ERANGE. */
- if (errno != 0)
- /* XXX - Do all operating systems have thread-safe strerror? */
- XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
- "error parsing \"%s\": %s (%d)",
- str, strerror(errno), errno);
-
- /* Look for out-of-range errors which didn't produce ERANGE. */
- if (i < min || i > max)
- XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
- "\"%s\" must be in range %d to %d", str, min, max);
- /* Check for unused characters. */
- if (end != str + stringLength)
- XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
- "\"%s\" contained trailing data", str);
-
- cleanup:
- errno = 0;
- if (env->fault_occurred)
- return 0;
- return (xmlrpc_int32) i;
- }
- static double
- xmlrpc_atod(xmlrpc_env *env, char *str, size_t stringLength)
- {
- double d;
- char *end;
- XMLRPC_ASSERT_ENV_OK(env);
- XMLRPC_ASSERT_PTR_OK(str);
- /* Suppress compiler warnings. */
- d = 0.0;
- /* Check for leading white space. */
- if (isspace((int)(str[0])))
- XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
- "\"%s\" must not contain whitespace", str);
-
- /* Convert the value. */
- end = str + stringLength;
- errno = 0;
- d = strtod(str, &end);
-
- /* Look for ERANGE. */
- if (errno != 0)
- /* XXX - Do all operating systems have thread-safe strerror? */
- XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
- "error parsing \"%s\": %s (%d)",
- str, strerror(errno), errno);
-
- /* Check for unused characters. */
- if (end != str + stringLength)
- XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
- "\"%s\" contained trailing data", str);
- cleanup:
- errno = 0;
- if (env->fault_occurred)
- return 0.0;
- return d;
- }
- /*=========================================================================
- ** make_string
- **=========================================================================
- ** Make an XML-RPC string.
- **
- ** SECURITY: We validate our UTF-8 first. This incurs a performance
- ** penalty, but ensures that we will never pass maliciously malformed
- ** UTF-8 data back up to the user layer, where it could wreak untold
- ** damange. Don't comment out this check unless you know *exactly* what
- ** you're doing. (Win32 developers who remove this check are *begging*
- ** to wind up on BugTraq, because many of the Win32 filesystem routines
- ** rely on an insecure UTF-8 decoder.)
- **
- ** XXX - This validation is redundant if the user chooses to convert
- ** UTF-8 data into a wchar_t string.
- */
- static xmlrpc_value *
- make_string(xmlrpc_env *env, char *cdata, size_t cdata_size)
- {
- #ifdef HAVE_UNICODE_WCHAR
- xmlrpc_validate_utf8(env, cdata, cdata_size);
- #endif
- if (env->fault_occurred)
- return NULL;
- return xmlrpc_build_value(env, "s#", cdata, cdata_size);
- }
- /*=========================================================================
- ** convert_value
- **=========================================================================
- ** Convert an XML element representing a value into an xmlrpc_value.
- */
- static xmlrpc_value *
- convert_array (xmlrpc_env *env, unsigned *depth, xml_element *elem);
- static xmlrpc_value *
- convert_struct(xmlrpc_env *env, unsigned *depth, xml_element *elem);
- static xmlrpc_value *
- convert_value(xmlrpc_env *env, unsigned *depth, xml_element *elem)
- {
- xml_element *child;
- int child_count;
- char *cdata, *child_name;
- size_t cdata_size, ascii_len;
- xmlrpc_mem_block *decoded;
- unsigned char *ascii_data;
- xmlrpc_value *retval;
- xmlrpc_int32 i;
- double d;
- XMLRPC_ASSERT_ENV_OK(env);
- XMLRPC_ASSERT(elem != NULL);
- /* Error-handling precoditions.
- ** If we haven't changed any of these from their default state, we're
- ** allowed to tail-call xmlrpc_build_value. */
- retval = NULL;
- decoded = NULL;
- /* Make sure we haven't recursed too deeply. */
- if (*depth > xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID))
- XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
- "Nested data structure too deep.");
- /* Validate our structure, and see whether we have a child element. */
- CHECK_NAME(env, elem, "value");
- child_count = xml_element_children_size(elem);
- if (child_count == 0) {
- /* We have no type element, so treat the value as a string. */
- cdata = xml_element_cdata(elem);
- cdata_size = xml_element_cdata_size(elem);
- return make_string(env, cdata, cdata_size);
- } else {
- /* We should have a type tag inside our value tag. */
- CHECK_CHILD_COUNT(env, elem, 1);
- child = xml_element_children(elem)[0];
-
- /* Parse our value-containing element. */
- child_name = xml_element_name(child);
- if (strcmp(child_name, "struct") == 0) {
- return convert_struct(env, depth, child);
- } else if (strcmp(child_name, "array") == 0) {
- CHECK_CHILD_COUNT(env, child, 1);
- return convert_array(env, depth, child);
- } else {
- CHECK_CHILD_COUNT(env, child, 0);
- cdata = xml_element_cdata(child);
- cdata_size = xml_element_cdata_size(child);
- if (strcmp(child_name, "i4") == 0 ||
- strcmp(child_name, "int") == 0)
- {
- i = xmlrpc_atoi(env, cdata, strlen(cdata),
- XMLRPC_INT32_MIN, XMLRPC_INT32_MAX);
- XMLRPC_FAIL_IF_FAULT(env);
- return xmlrpc_build_value(env, "i", i);
- } else if (strcmp(child_name, "string") == 0) {
- return make_string(env, cdata, cdata_size);
- } else if (strcmp(child_name, "boolean") == 0) {
- i = xmlrpc_atoi(env, cdata, strlen(cdata), 0, 1);
- XMLRPC_FAIL_IF_FAULT(env);
- return xmlrpc_build_value(env, "b", (xmlrpc_bool) i);
- } else if (strcmp(child_name, "double") == 0) {
- d = xmlrpc_atod(env, cdata, strlen(cdata));
- XMLRPC_FAIL_IF_FAULT(env);
- return xmlrpc_build_value(env, "d", d);
- } else if (strcmp(child_name, "dateTime.iso8601") == 0) {
- return xmlrpc_build_value(env, "8", cdata);
- } else if (strcmp(child_name, "base64") == 0) {
- /* No more tail calls once we do this! */
- decoded = xmlrpc_base64_decode(env, cdata, cdata_size);
- XMLRPC_FAIL_IF_FAULT(env);
- ascii_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char,
- decoded);
- ascii_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char,
- decoded);
- retval = xmlrpc_build_value(env, "6", ascii_data, ascii_len);
- XMLRPC_FAIL_IF_FAULT(env);
- } else {
- XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
- "Unknown value type <%s>", child_name);
- }
- }
- }
- cleanup:
- if (decoded)
- xmlrpc_mem_block_free(decoded);
- if (env->fault_occurred) {
- if (retval)
- xmlrpc_DECREF(retval);
- return NULL;
- }
- return retval;
- }
- /*=========================================================================
- ** convert_array
- **=========================================================================
- ** Convert an XML element representing an array into an xmlrpc_value.
- */
- static xmlrpc_value *
- convert_array(xmlrpc_env *env, unsigned *depth, xml_element *elem)
- {
- xml_element *data, **values, *value;
- xmlrpc_value *array, *item;
- int size, i;
- XMLRPC_ASSERT_ENV_OK(env);
- XMLRPC_ASSERT(elem != NULL);
- /* Set up our error-handling preconditions. */
- item = NULL;
- (*depth)++;
- /* Allocate an array to hold our values. */
- array = xmlrpc_build_value(env, "()");
- XMLRPC_FAIL_IF_FAULT(env);
- /* We don't need to check our element name--our callers do that. */
- CHECK_CHILD_COUNT(env, elem, 1);
- data = xml_element_children(elem)[0];
- CHECK_NAME(env, data, "data");
-
- /* Iterate over our children. */
- values = xml_element_children(data);
- size = xml_element_children_size(data);
- for (i = 0; i < size; i++) {
- value = values[i];
- item = convert_value(env, depth, value);
- XMLRPC_FAIL_IF_FAULT(env);
- xmlrpc_array_append_item(env, array, item);
- xmlrpc_DECREF(item);
- item = NULL;
- XMLRPC_FAIL_IF_FAULT(env);
- }
- cleanup:
- (*depth)--;
- if (item)
- xmlrpc_DECREF(item);
- if (env->fault_occurred) {
- if (array)
- xmlrpc_DECREF(array);
- return NULL;
- }
- return array;
- }
- /*=========================================================================
- ** convert_struct
- **=========================================================================
- ** Convert an XML element representing a struct into an xmlrpc_value.
- */
- static xmlrpc_value *
- convert_struct(xmlrpc_env *env, unsigned *depth, xml_element *elem)
- {
- xmlrpc_value *strct, *key, *value;
- xml_element **members, *member, *name_elem, *value_elem;
- int size, i;
- char *cdata;
- size_t cdata_size;
- XMLRPC_ASSERT_ENV_OK(env);
- XMLRPC_ASSERT(elem != NULL);
- /* Set up our error-handling preconditions. */
- key = value = NULL;
- (*depth)++;
- /* Allocate an array to hold our members. */
- strct = xmlrpc_struct_new(env);
- XMLRPC_FAIL_IF_FAULT(env);
- /* Iterate over our children, extracting key/value pairs. */
- /* We don't need to check our element name--our callers do that. */
- members = xml_element_children(elem);
- size = xml_element_children_size(elem);
- for (i = 0; i < size; i++) {
- member = members[i];
- CHECK_NAME(env, member, "member");
- CHECK_CHILD_COUNT(env, member, 2);
- /* Get our key. */
- name_elem = get_child_by_name(env, member, "name");
- XMLRPC_FAIL_IF_FAULT(env);
- CHECK_CHILD_COUNT(env, name_elem, 0);
- cdata = xml_element_cdata(name_elem);
- cdata_size = xml_element_cdata_size(name_elem);
- key = make_string(env, cdata, cdata_size);
- XMLRPC_FAIL_IF_FAULT(env);
- /* Get our value. */
- value_elem = get_child_by_name(env, member, "value");
- XMLRPC_FAIL_IF_FAULT(env);
- value = convert_value(env, depth, value_elem);
- XMLRPC_FAIL_IF_FAULT(env);
- /* Add the key/value pair to our struct. */
- xmlrpc_struct_set_value_v(env, strct, key, value);
- XMLRPC_FAIL_IF_FAULT(env);
- /* Release our references & memory, and restore our invariants. */
- xmlrpc_DECREF(key);
- key = NULL;
- xmlrpc_DECREF(value);
- value = NULL;
- }
-
- cleanup:
- (*depth)--;
- if (key)
- xmlrpc_DECREF(key);
- if (value)
- xmlrpc_DECREF(value);
- if (env->fault_occurred) {
- if (strct)
- xmlrpc_DECREF(strct);
- return NULL;
- }
- return strct;
- }
- /*=========================================================================
- ** convert_params
- **=========================================================================
- ** Convert an XML element representing a list of params into an
- ** xmlrpc_value (of type array).
- */
- static xmlrpc_value *
- convert_params(xmlrpc_env *env, unsigned *depth, xml_element *elem)
- {
- xmlrpc_value *array, *item;
- int size, i;
- xml_element **params, *param, *value;
- XMLRPC_ASSERT_ENV_OK(env);
- XMLRPC_ASSERT(elem != NULL);
- /* Set up our error-handling preconditions. */
- item = NULL;
- /* Allocate an array to hold our parameters. */
- array = xmlrpc_build_value(env, "()");
- XMLRPC_FAIL_IF_FAULT(env);
- /* We're responsible for checking our own element name. */
- CHECK_NAME(env, elem, "params");
- /* Iterate over our children. */
- size = xml_element_children_size(elem);
- params = xml_element_children(elem);
- for (i = 0; i < size; i++) {
- param = params[i];
- CHECK_NAME(env, param, "param");
- CHECK_CHILD_COUNT(env, param, 1);
- value = xml_element_children(param)[0];
- item = convert_value(env, depth, value);
- XMLRPC_FAIL_IF_FAULT(env);
- xmlrpc_array_append_item(env, array, item);
- xmlrpc_DECREF(item);
- item = NULL;
- XMLRPC_FAIL_IF_FAULT(env);
- }
- cleanup:
- if (env->fault_occurred) {
- if (array)
- xmlrpc_DECREF(array);
- if (item)
- xmlrpc_DECREF(item);
- return NULL;
- }
- return array;
- }
- static void
- parseCallXml(xmlrpc_env * const envP,
- const char * const xmlData,
- size_t const xmlLen,
- xml_element ** const callElemP) {
- xmlrpc_env env;
- xmlrpc_env_init(&env);
- *callElemP = xml_parse(&env, xmlData, xmlLen);
- if (env.fault_occurred)
- xmlrpc_env_set_fault_formatted(
- envP, env.fault_code, "Call is not valid XML. %s",
- env.fault_string);
- xmlrpc_env_clean(&env);
- }
- /*=========================================================================
- ** xmlrpc_parse_call
- **=========================================================================
- ** Given some XML text, attempt to parse it as an XML-RPC call. Return
- ** a newly allocated xmlrpc_call structure (or NULL, if an error occurs).
- ** The two output variables will contain either valid values (which
- ** must free() and xmlrpc_DECREF(), respectively) or NULLs (if an error
- ** occurs).
- */
- void
- xmlrpc_parse_call(xmlrpc_env * const envP,
- const char * const xml_data,
- size_t const xml_len,
- const char ** const out_method_nameP,
- xmlrpc_value ** const out_param_arrayPP) {
- xml_element *call_elem, *name_elem, *params_elem;
- char *cdata;
- unsigned depth;
- size_t call_child_count;
- char * outMethodName;
- xmlrpc_value * outParamArrayP;
- XMLRPC_ASSERT_ENV_OK(envP);
- XMLRPC_ASSERT(xml_data != NULL);
- XMLRPC_ASSERT(out_method_nameP != NULL && out_param_arrayPP != NULL);
- /* Set up our error-handling preconditions. */
- outMethodName = NULL;
- outParamArrayP = NULL;
- call_elem = NULL;
- /* SECURITY: Last-ditch attempt to make sure our content length is legal.
- ** XXX - This check occurs too late to prevent an attacker from creating
- ** an enormous memory block in RAM, so you should try to enforce it
- ** *before* reading any data off the network. */
- if (xml_len > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
- XMLRPC_FAIL(envP, XMLRPC_LIMIT_EXCEEDED_ERROR,
- "XML-RPC request too large");
- parseCallXml(envP, xml_data, xml_len, &call_elem);
- XMLRPC_FAIL_IF_FAULT(envP);
- /* Pick apart and verify our structure. */
- CHECK_NAME(envP, call_elem, "methodCall");
- call_child_count = xml_element_children_size(call_elem);
- if (call_child_count != 2 && call_child_count != 1)
- XMLRPC_FAIL1(envP, XMLRPC_PARSE_ERROR,
- "Expected <methodCall> to have 1 or 2 children, found %d",
- call_child_count);
- /* Extract the method name.
- ** SECURITY: We make sure the method name is valid UTF-8. */
- name_elem = get_child_by_name(envP, call_elem, "methodName");
- XMLRPC_FAIL_IF_FAULT(envP);
- CHECK_CHILD_COUNT(envP, name_elem, 0);
- cdata = xml_element_cdata(name_elem);
- #ifdef HAVE_UNICODE_WCHAR
- xmlrpc_validate_utf8(envP, cdata, strlen(cdata));
- XMLRPC_FAIL_IF_FAULT(envP);
- #endif /* HAVE_UNICODE_WCHAR */
- outMethodName = malloc(strlen(cdata) + 1);
- XMLRPC_FAIL_IF_NULL(outMethodName, envP, XMLRPC_INTERNAL_ERROR,
- "Could not allocate memory for method name");
- strcpy(outMethodName, cdata);
-
- /* Convert our parameters. */
- if (call_child_count == 1) {
- /* Workaround for Ruby XML-RPC and old versions of xmlrpc-epi. */
- outParamArrayP = xmlrpc_build_value(envP, "()");
- XMLRPC_FAIL_IF_FAULT(envP);
- } else {
- params_elem = get_child_by_name(envP, call_elem, "params");
- XMLRPC_FAIL_IF_FAULT(envP);
- depth = 0;
- outParamArrayP = convert_params(envP, &depth, params_elem);
- XMLRPC_ASSERT(depth == 0);
- XMLRPC_FAIL_IF_FAULT(envP);
- }
- cleanup:
- if (call_elem)
- xml_element_free(call_elem);
- if (envP->fault_occurred) {
- if (outMethodName)
- free(outMethodName);
- if (outParamArrayP)
- xmlrpc_DECREF(outParamArrayP);
- outMethodName = NULL;
- outParamArrayP = NULL;
- }
- *out_method_nameP = outMethodName;
- *out_param_arrayPP = outParamArrayP;
- }
- /*=========================================================================
- ** xmlrpc_parse_response
- **=========================================================================
- ** Given some XML text, attempt to parse it as an XML-RPC response.
- ** If the response is a regular, valid response, return a new reference
- ** to the appropriate value. If the response is a fault, or an error
- ** occurs during processing, return NULL and set up env appropriately.
- */
- xmlrpc_value *
- xmlrpc_parse_response(xmlrpc_env *env,
- const char *xml_data,
- size_t xml_len) {
- xml_element *response, *child, *value;
- unsigned depth;
- xmlrpc_value *params, *retval, *fault;
- int retval_incremented;
- xmlrpc_value *fault_code_value, *fault_str_value;
- xmlrpc_int32 fault_code;
- char *fault_str;
- XMLRPC_ASSERT_ENV_OK(env);
- XMLRPC_ASSERT(xml_data != NULL);
- /* Set up our error-handling preconditions. */
- response = NULL;
- params = fault = NULL;
- retval_incremented = 0;
- /* SECURITY: Last-ditch attempt to make sure our content length is legal.
- ** XXX - This check occurs too late to prevent an attacker from creating
- ** an enormous memory block in RAM, so you should try to enforce it
- ** *before* reading any data off the network. */
- if (xml_len > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
- XMLRPC_FAIL(env, XMLRPC_LIMIT_EXCEEDED_ERROR,
- "XML-RPC response too large");
- /* SECURITY: Set up our recursion depth counter. */
- depth = 0;
- /* Parse our XML data. */
- response = xml_parse(env, xml_data, xml_len);
- XMLRPC_FAIL_IF_FAULT(env);
- /* Pick apart and verify our structure. */
- CHECK_NAME(env, response, "methodResponse");
- CHECK_CHILD_COUNT(env, response, 1);
- child = xml_element_children(response)[0];
- /* Parse the response itself. */
- if (strcmp("params", xml_element_name(child)) == 0) {
- /* Convert our parameter list. */
- params = convert_params(env, &depth, child);
- XMLRPC_FAIL_IF_FAULT(env);
- /* Extract the return value, and jiggle our reference counts. */
- xmlrpc_parse_value(env, params, "(V)", &retval);
- XMLRPC_FAIL_IF_FAULT(env);
- xmlrpc_INCREF(retval);
- retval_incremented = 1;
- } else if (strcmp("fault", xml_element_name(child)) == 0) {
-
- /* Convert our fault structure. */
- CHECK_CHILD_COUNT(env, child, 1);
- value = xml_element_children(child)[0];
- fault = convert_value(env, &depth, value);
- XMLRPC_FAIL_IF_FAULT(env);
- XMLRPC_TYPE_CHECK(env, fault, XMLRPC_TYPE_STRUCT);
- /* Get our fault code. */
- fault_code_value = xmlrpc_struct_get_value(env, fault, "faultCode");
- XMLRPC_FAIL_IF_FAULT(env);
- xmlrpc_parse_value(env, fault_code_value, "i", &fault_code);
- XMLRPC_FAIL_IF_FAULT(env);
- /* Get our fault string. */
- fault_str_value = xmlrpc_struct_get_value(env, fault, "faultString");
- XMLRPC_FAIL_IF_FAULT(env);
- xmlrpc_parse_value(env, fault_str_value, "s", &fault_str);
- XMLRPC_FAIL_IF_FAULT(env);
- /* Return our fault. */
- XMLRPC_FAIL(env, fault_code, fault_str);
- } else {
- XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
- "Expected <params> or <fault> in <methodResponse>");
- }
- /* Sanity-check our depth-counting code. */
- XMLRPC_ASSERT(depth == 0);
-
- cleanup:
- if (response)
- xml_element_free(response);
- if (params)
- xmlrpc_DECREF(params);
- if (fault)
- xmlrpc_DECREF(fault);
- if (env->fault_occurred) {
- if (retval_incremented)
- xmlrpc_DECREF(retval);
- return NULL;
- }
- return retval;
- }
|