void xmlrpc_parse_response2(xmlrpc_env * const envP, const char * const xmlData, size_t const xmlDataLen, xmlrpc_value ** const resultPP, int * const faultCodeP, const char ** const faultStringP) { /*---------------------------------------------------------------------------- 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 as *resultP and return NULL as *faultStringP and nothing as *faultCodeP. If the response is valid, but indicates a failure of the RPC, return the fault string in newly malloc'ed space as *faultStringP and the fault code as *faultCodeP and nothing as *resultP. If the XML text is not a valid response or something prevents us from parsing it, return a description of the error as *envP and nothing else. -----------------------------------------------------------------------------*/ xml_element * response; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(xmlData != 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, so you should try to enforce it ** *before* reading any data off the network. */ if (xmlDataLen > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)) xmlrpc_env_set_fault_formatted( envP, XMLRPC_LIMIT_EXCEEDED_ERROR, "XML-RPC response too large. Our limit is %u characters. " "We got %u characters", xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID), xmlDataLen); else { xmlrpc_env env; xmlrpc_env_init(&env); xml_parse(&env, xmlData, xmlDataLen, &response); if (env.fault_occurred) setParseFault(envP, "Not valid XML. %s", env.fault_string); else { /* Pick apart and verify our structure. */ if (xmlrpc_streq(xml_element_name(response), "methodResponse")) { parseMethodResponseElt(envP, response, resultPP, faultCodeP, faultStringP); } else setParseFault(envP, "XML-RPC response must consist of a " "<methodResponse> element. " "This has a <%s> instead.", xml_element_name(response)); xml_element_free(response); } xmlrpc_env_clean(&env); } }
void processCall2(const registry * const registryP, FILE * const callFileP, unsigned int const callSize, bool const sendCookie, string const& authCookie, FILE * const respFileP) { if (callSize > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)) throw(xmlrpc_c::fault(string("XML-RPC call is too large"), fault::CODE_LIMIT_EXCEEDED)); else { string const callXml(getHttpBody(callFileP, callSize)); string responseXml; try { registryP->processCall(callXml, &responseXml); } catch (exception const& e) { throw(httpError(500, e.what())); } writeNormalHttpResp(respFileP, sendCookie, authCookie, responseXml); } }
static void test_nesting_limit (void) { xmlrpc_env env; xmlrpc_value *val; xmlrpc_env_init(&env); /* Test with an adequate limit for a result value which is an array which contains an element which is a struct, whose values are simple: 3. */ xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, 3); val = xmlrpc_parse_response(&env, good_response_xml, strlen(good_response_xml)); TEST_NO_FAULT(&env); TEST(val != NULL); xmlrpc_DECREF(val); /* Test with an inadequate limit. */ xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, 2); val = xmlrpc_parse_response(&env, good_response_xml, strlen(good_response_xml)); TEST_FAULT(&env, XMLRPC_PARSE_ERROR); /* BREAKME - Will change. */ TEST(val == NULL); /* Reset the default limit. */ xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, XMLRPC_NESTING_LIMIT_DEFAULT); TEST(xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID) == XMLRPC_NESTING_LIMIT_DEFAULT); xmlrpc_env_clean(&env); }
static xmlrpc_value * convert_params(xmlrpc_env * const envP, const xml_element * const elemP) { /*---------------------------------------------------------------------------- Convert an XML element representing a list of parameters (i.e. a <params> element) to an xmlrpc_value of type array. Note that an array is normally represented in XML by a <value> element. We use type xmlrpc_value to represent the parameter list just for convenience. -----------------------------------------------------------------------------*/ xmlrpc_value *array, *item; int size, i; xml_element **params, *param, *value; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(elemP != NULL); /* Set up our error-handling preconditions. */ array = item = NULL; /* Allocate an array to hold our parameters. */ array = xmlrpc_build_value(envP, "()"); XMLRPC_FAIL_IF_FAULT(envP); /* We're responsible for checking our own element name. */ CHECK_NAME(envP, elemP, "params"); /* Iterate over our children. */ size = xml_element_children_size(elemP); params = xml_element_children(elemP); for (i = 0; i < size; ++i) { unsigned int const maxNest = xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID); param = params[i]; CHECK_NAME(envP, param, "param"); CHECK_CHILD_COUNT(envP, param, 1); value = xml_element_children(param)[0]; CHECK_NAME(envP, value, "value"); xmlrpc_parseValue(envP, maxNest, value, &item); XMLRPC_FAIL_IF_FAULT(envP); xmlrpc_array_append_item(envP, array, item); xmlrpc_DECREF(item); item = NULL; XMLRPC_FAIL_IF_FAULT(envP); } cleanup: if (envP->fault_occurred) { if (array) xmlrpc_DECREF(array); if (item) xmlrpc_DECREF(item); return NULL; } return array; }
static void processCall(TSession * const abyssSessionP, size_t const contentSize, xmlrpc_call_processor xmlProcessor, void * const xmlProcessorArg, bool const wantChunk, ResponseAccessCtl const accessControl, const char * const trace) { /*---------------------------------------------------------------------------- Handle an RPC request. This is an HTTP request that has the proper form to be an XML-RPC call. The text of the call is available through the Abyss session 'abyssSessionP'. Its content length is 'contentSize' bytes. -----------------------------------------------------------------------------*/ xmlrpc_env env; if (trace) fprintf(stderr, "xmlrpc_server_abyss URI path handler processing RPC.\n"); xmlrpc_env_init(&env); if (contentSize > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)) xmlrpc_env_set_fault_formatted( &env, XMLRPC_LIMIT_EXCEEDED_ERROR, "XML-RPC request too large (%u bytes)", (unsigned)contentSize); else { xmlrpc_mem_block * body; /* Read XML data off the wire. */ getBody(&env, abyssSessionP, contentSize, trace, &body); if (!env.fault_occurred) { xmlrpc_mem_block * output; /* Process the RPC. */ xmlProcessor( &env, xmlProcessorArg, XMLRPC_MEMBLOCK_CONTENTS(char, body), XMLRPC_MEMBLOCK_SIZE(char, body), abyssSessionP, &output); if (!env.fault_occurred) { /* Send out the result. */ sendResponse(&env, abyssSessionP, XMLRPC_MEMBLOCK_CONTENTS(char, output), XMLRPC_MEMBLOCK_SIZE(char, output), wantChunk, accessControl); XMLRPC_MEMBLOCK_FREE(char, output); } XMLRPC_MEMBLOCK_FREE(char, body); }
void xmlrpc_parse_call(xmlrpc_env * const envP, const char * const xmlData, size_t const xmlDataLen, const char ** const methodNameP, xmlrpc_value ** const paramArrayPP) { /*---------------------------------------------------------------------------- Given some XML text, attempt to parse it as an XML-RPC call. Return as *methodNameP the name of the method identified in the call and as *paramArrayPP the parameter list as an XML-RPC array. Caller must free() and xmlrpc_DECREF() these, respectively). -----------------------------------------------------------------------------*/ XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(xmlData != NULL); XMLRPC_ASSERT(methodNameP != NULL && paramArrayPP != 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, so you should try to enforce it *before* reading any data off the network. */ if (xmlDataLen > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)) xmlrpc_env_set_fault_formatted( envP, XMLRPC_LIMIT_EXCEEDED_ERROR, "XML-RPC request too large. Max allowed is %u bytes", (unsigned)xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)); else { xml_element * callElemP; parseCallXml(envP, xmlData, xmlDataLen, &callElemP); if (!envP->fault_occurred) { parseCallChildren(envP, callElemP, methodNameP, paramArrayPP); xml_element_free(callElemP); } } if (envP->fault_occurred) { /* Should not be necessary, but for backward compatibility: */ *methodNameP = NULL; *paramArrayPP = NULL; } }
void xmlrpc_parse_value_xml(xmlrpc_env * const envP, const char * const xmlData, size_t const xmlDataLen, xmlrpc_value ** const valuePP) { /*---------------------------------------------------------------------------- Compute the xmlrpc_value represented by the XML document 'xmlData' (of length 'xmlDataLen' characters), which must consist of a single <value> element. Return that xmlrpc_value. We call convert_array() and convert_struct(), which may ultimately call us recursively. Don't recurse any more than 'maxRecursion' times. This isn't generally useful in XML-RPC programs, because such programs parse a whole XML-RPC call or response document, and never see the XML text of just a <value> element. But a program may do some weird form of XML-RPC processing or just borrow Xmlrpc-c's value serialization facilities for something unrelated to XML-RPC. In any case, it makes sense to have an inverse of xmlrpc_serialize_value2(), which generates XML text from an xmlrpc_value. -----------------------------------------------------------------------------*/ xmlrpc_env env; xml_element * valueEltP; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(xmlData != NULL); xmlrpc_env_init(&env); xml_parse(&env, xmlData, xmlDataLen, &valueEltP); if (env.fault_occurred) { setParseFault(envP, "Not valid XML. %s", env.fault_string); } else { if (xmlrpc_streq(xml_element_name(valueEltP), "value")) { unsigned int const maxRecursion = (unsigned int) xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID); xmlrpc_parseValue(envP, maxRecursion, valueEltP, valuePP); } else setParseFault(envP, "XML-RPC value XML document must consist of " "a <value> element. This has a <%s> instead.", xml_element_name(valueEltP)); xml_element_free(valueEltP); } xmlrpc_env_clean(&env); }
static void processCall(TSession * const abyssSessionP, size_t const contentSize, xmlrpc_registry * const registryP, bool const wantChunk, const char * const trace) { /*---------------------------------------------------------------------------- Handle an RPC request. This is an HTTP request that has the proper form to be one of our RPCs. Its content length is 'contentSize' bytes. -----------------------------------------------------------------------------*/ xmlrpc_env env; if (trace) fprintf(stderr, "xmlrpc_server_abyss URI path handler processing RPC.\n"); xmlrpc_env_init(&env); if (contentSize > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)) xmlrpc_env_set_fault_formatted( &env, XMLRPC_LIMIT_EXCEEDED_ERROR, "XML-RPC request too large (%ld bytes)", (long)contentSize); else { xmlrpc_mem_block *body=0; /* Read XML data off the wire. */ getBody(&env, abyssSessionP, contentSize, trace, &body); if (!env.fault_occurred) { xmlrpc_mem_block * output; /* Process the RPC. */ output = xmlrpc_registry_process_call( &env, registryP, NULL, XMLRPC_MEMBLOCK_CONTENTS(char, body), XMLRPC_MEMBLOCK_SIZE(char, body)); if (!env.fault_occurred) { /* Send out the result. */ sendXmlData(&env, abyssSessionP, XMLRPC_MEMBLOCK_CONTENTS(char, output), XMLRPC_MEMBLOCK_SIZE(char, output), wantChunk); XMLRPC_MEMBLOCK_FREE(char, output); } XMLRPC_MEMBLOCK_FREE(char, body); }
static void parseFaultElement(xmlrpc_env * const envP, const xml_element * const faultElement, int * const faultCodeP, const char ** const faultStringP) { unsigned int const maxRecursion = (unsigned int) xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID); XMLRPC_ASSERT(xmlrpc_streq(xml_element_name(faultElement), "fault")); if (xml_element_children_size(faultElement) != 1) setParseFault(envP, "<fault> element should have 1 child, " "but it has %u.", xml_element_children_size(faultElement)); else { xml_element * const faultValueP = xml_element_children(faultElement)[0]; const char * const elemName = xml_element_name(faultValueP); if (!xmlrpc_streq(elemName, "value")) setParseFault(envP, "<fault> contains a <%s> element. " "Only <value> makes sense.", elemName); else { xmlrpc_value * faultVP; xmlrpc_parseValue(envP, maxRecursion, faultValueP, &faultVP); if (!envP->fault_occurred) { interpretFaultValue(envP, faultVP, faultCodeP, faultStringP); xmlrpc_DECREF(faultVP); } } } }
/************************************** * ProcessRequest * Procesa una peticion *************************************/ int XmlHandler::ProcessRequest(TRequestInfo *req,TSession * const ses) { xmlrpc_env env; int inputLen; char *method; xmlrpc_value *params = NULL; timeval tv; Log(">ProcessRequest [uri:%s]\n",req->uri); //Init timer getUpdDifTime(&tv); //Creamos un enviroment xmlrpc_env_init(&env); //Si no es post if (req->method != m_post) //Mandamos error return XmlRpcServer::SendError(ses, 405, "Only POST allowed"); //Obtenemos el content type const char * content_type = RequestHeaderValue(ses, (char*)"content-type"); //Si no es el bueno if (content_type == NULL || strcmp(content_type, "text/xml") != 0) return XmlRpcServer::SendError(ses, 400, "Wrong content-type"); //Obtenemos el content length const char * content_length = RequestHeaderValue(ses, (char*)"content-length"); //Si no hay if (content_length == NULL) return XmlRpcServer::SendError(ses,411,"No content-length"); //Obtenemos el entero inputLen = atoi(content_length); //Tiene que ser mayor que cero if ((inputLen < 0) || (inputLen > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))) return XmlRpcServer::SendError(ses,400,"Size limit"); //Creamos un buffer para el body char * buffer = (char *) malloc(inputLen); if (!XmlRpcServer::GetBody(ses,buffer,inputLen)) { //LIberamos el buffer free(buffer); //Y salimos sin devolver nada Log("Operation timedout\n"); return 1; } //Get method name xmlrpc_parse_call(&env,buffer,inputLen,(const char**)&method,¶ms); Log("-ProcessRequest [method:%s]\n",method); //Free name and params free(method); xmlrpc_DECREF(params); //Generamos la respuesta xmlrpc_mem_block *output = xmlrpc_registry_process_call( &env, registry, NULL, buffer, inputLen ); //Si todo ha ido bien if (!env.fault_occurred) { //POnemos el content type ResponseContentType(ses, (char*)"text/xml; charset=\"utf-8\""); //Y mandamos la respuesta XmlRpcServer::SendResponse(ses,200,XMLRPC_MEMBLOCK_CONTENTS(char, output), XMLRPC_MEMBLOCK_SIZE(char, output)); } else
/* * This is a blocking function that merely sits on the request queue * for our URI and processes them one at a time. Once a request comes * in, we check it for content-type, content-length, and verb. As long * as the initial validations are done, we pass the request to the * processRPCCall() function, which collects the body of the request * and processes it. If we get an error back other than network type, * we are responsible for notifing the client. */ DWORD DoReceiveRequests( IN HANDLE hReqQueue, const xmlrpc_server_httpsys_parms * const parmsP ) { ULONG result; HTTP_REQUEST_ID requestId; DWORD bytesRead; PHTTP_REQUEST pRequest; PCHAR pRequestBuffer; ULONG RequestBufferLength; xmlrpc_env env; char szHeaderBuf[255]; long lContentLength; // Allocate a 2K buffer. Should be good for most requests, we'll grow // this if required. We also need space for a HTTP_REQUEST structure. RequestBufferLength = sizeof(HTTP_REQUEST) + 2048; pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength ); if (pRequestBuffer == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } pRequest = (PHTTP_REQUEST)pRequestBuffer; // Wait for a new request -- This is indicated by a NULL request ID. HTTP_SET_NULL_ID( &requestId ); for(;;) { RtlZeroMemory(pRequest, RequestBufferLength); result = HttpReceiveHttpRequest( hReqQueue, // Req Queue requestId, // Req ID 0, // Flags pRequest, // HTTP request buffer RequestBufferLength,// req buffer length &bytesRead, // bytes received NULL // LPOVERLAPPED ); if(NO_ERROR == result) { // Got a request with a filled buffer. switch(pRequest->Verb) { case HttpVerbPOST: TraceW(L"Got a POST request for %ws", pRequest->CookedUrl.pFullUrl); //Check if we need use authorization. if(parmsP->authfn) { xmlrpc_env_init(&env); if(pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization].RawValueLength < 6) { xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR, "Authorization header too short."); } else { //unencode the headers if(_strnicmp( "basic", pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization].pRawValue,5) !=0) { #ifndef NDEBUG PCHAR pTmp = (PCHAR) ALLOC_MEM(pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].RawValueLength + 1 ); if( pTmp ) { strncpy(pTmp, pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].pRawValue, pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].RawValueLength ); pTmp[pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].RawValueLength] = 0; TraceA("Got HEADER [%s]",pTmp); FREE_MEM(pTmp); } #endif /* #ifndef NDEBUG */ xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR, "Authorization header does not start " "with type 'basic'!"); } else { xmlrpc_mem_block * decoded; decoded = xmlrpc_base64_decode( &env, pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].pRawValue+6, pRequest->Headers.KnownHeaders[ HttpHeaderAuthorization ].RawValueLength-6); if(!env.fault_occurred) { char *pDecodedStr; char *pUser; char *pPass; char *pColon; pDecodedStr = (char*) malloc(decoded->_size+1); memcpy(pDecodedStr, decoded->_block, decoded->_size); pDecodedStr[decoded->_size]='\0'; pUser = pPass = pDecodedStr; pColon=strchr(pDecodedStr,':'); if(pColon) { *pColon='\0'; pPass=pColon+1; //The authfn should set env to //fail if auth is denied. parmsP->authfn(&env,pUser,pPass); } else { xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR, "Decoded auth not of the correct " "format."); } free(pDecodedStr); } if(decoded) XMLRPC_MEMBLOCK_FREE(char, decoded); } } if(env.fault_occurred) { //request basic authorization, as the user //did not provide it. xmlrpc_env_clean(&env); TraceW(L"POST request did not provide valid " L"authorization header."); result = SendHttpResponseAuthRequired( hReqQueue, pRequest); break; } xmlrpc_env_clean(&env); } //Check content type to make sure it is text/xml. memcpy(szHeaderBuf, pRequest->Headers.KnownHeaders[ HttpHeaderContentType ].pRawValue, pRequest->Headers.KnownHeaders[ HttpHeaderContentType ].RawValueLength); szHeaderBuf[pRequest->Headers.KnownHeaders[ HttpHeaderContentType ].RawValueLength] = '\0'; if (_stricmp(szHeaderBuf,"text/xml")!=0) { //We handle only text/xml data. Anything else //is not valid. TraceW(L"POST request had an unrecognized " L"content-type: %s", szHeaderBuf); result = SendHttpResponse( hReqQueue, pRequest, 400, "Bad Request", NULL ); break; } //Check content length to make sure it exists and //is not too big. memcpy(szHeaderBuf, pRequest->Headers.KnownHeaders[ HttpHeaderContentLength ].pRawValue, pRequest->Headers.KnownHeaders[ HttpHeaderContentLength ].RawValueLength); szHeaderBuf[pRequest->Headers.KnownHeaders[ HttpHeaderContentLength ].RawValueLength]='\0'; lContentLength = atol(szHeaderBuf); if (lContentLength<=0) { //Make sure a content length was supplied. TraceW(L"POST request did not include a " L"content-length", szHeaderBuf); result = SendHttpResponse( hReqQueue, pRequest, 411, "Length Required", NULL ); break; } if((size_t) lContentLength > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)) { //Content-length is too big for us to handle TraceW(L"POST request content-length is too big " L"for us to handle: %d bytes", lContentLength); result = SendHttpResponse( hReqQueue, pRequest, 500, "content-length too large", NULL ); break; } //our initial validations of POST, content-type, //and content-length all check out. Collect and //pass the complete buffer to the XMLRPC-C library xmlrpc_env_init(&env); processRPCCall(&env,hReqQueue, pRequest); if (env.fault_occurred) { //if we fail and it is anything other than a //network error, we should return a failure //response to the client. if (env.fault_code != XMLRPC_NETWORK_ERROR) { if (env.fault_string) result = SendHttpResponse( hReqQueue, pRequest, 500, env.fault_string, NULL ); else result = SendHttpResponse( hReqQueue, pRequest, 500, "Unknown Error", NULL ); } } xmlrpc_env_clean(&env); break; default: //We handle only POST data. Anything else is not valid. TraceW(L"Got an unrecognized Verb request for URI %ws", pRequest->CookedUrl.pFullUrl); result = SendHttpResponse( hReqQueue, pRequest, 405, "Method Not Allowed", NULL ); break; } if(result != NO_ERROR) { break; } // Reset the Request ID so that we pick up the next request. HTTP_SET_NULL_ID( &requestId ); } else if(result == ERROR_MORE_DATA) { // The input buffer was too small to hold the request headers // We have to allocate more buffer & call the API again. // // When we call the API again, we want to pick up the request // that just failed. This is done by passing a RequestID. // This RequestID is picked from the old buffer. requestId = pRequest->RequestId; // Free the old buffer and allocate a new one. RequestBufferLength = bytesRead; FREE_MEM( pRequestBuffer ); pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength ); if (pRequestBuffer == NULL) { result = ERROR_NOT_ENOUGH_MEMORY; break; } pRequest = (PHTTP_REQUEST)pRequestBuffer; } else if(ERROR_CONNECTION_INVALID == result && !HTTP_IS_NULL_ID(&requestId)) { // The TCP connection got torn down by the peer when we were // trying to pick up a request with more buffer. We'll just move // onto the next request. HTTP_SET_NULL_ID( &requestId ); } else { break; } } // for(;;) if(pRequestBuffer) { FREE_MEM( pRequestBuffer ); } return result; }