/** * Processes identity request body. * * @param connp * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed. */ int htp_connp_REQ_BODY_IDENTITY(htp_connp_t *connp) { htp_tx_data_t d; d.tx = connp->in_tx; d.data = &connp->in_current_data[connp->in_current_offset]; d.len = 0; for (;;) { IN_NEXT_BYTE(connp); if (connp->in_next_byte == -1) { // End of chunk int rc = htp_req_run_hook_body_data(connp, &d); if (rc != HOOK_OK) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request body data callback returned error (%d)", rc); return HTP_ERROR; } // Ask for more data return HTP_DATA; } else { connp->in_tx->request_message_len++; connp->in_tx->request_entity_len++; connp->in_body_data_left--; d.len++; if (connp->in_body_data_left == 0) { // End of body int rc = htp_req_run_hook_body_data(connp, &d); if (rc != HOOK_OK) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request body data callback returned error (%d)", rc); return HTP_ERROR; } // Done connp->in_state = htp_connp_REQ_IDLE; connp->in_tx->progress = TX_PROGRESS_WAIT; return HTP_OK; } } } }
/** * Processes a chunk of data. * * @param connp * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed. */ int htp_connp_REQ_BODY_CHUNKED_DATA(htp_connp_t *connp) { htp_tx_data_t d; d.tx = connp->in_tx; d.data = &connp->in_current_data[connp->in_current_offset]; d.len = 0; for (;;) { IN_NEXT_BYTE(connp); if (connp->in_next_byte == -1) { // Send data to callbacks int rc = htp_req_run_hook_body_data(connp, &d); if (rc != HOOK_OK) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request body data callback returned error (%d)", rc); return HTP_ERROR; } // Ask for more data return HTP_DATA; } else { connp->in_tx->request_message_len++; connp->in_tx->request_entity_len++; connp->in_chunked_length--; d.len++; if (connp->in_chunked_length == 0) { // End of data chunk // Send data to callbacks int rc = htp_req_run_hook_body_data(connp, &d); if (rc != HOOK_OK) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request body data callback returned error (%d)", rc); return HTP_ERROR; } connp->in_state = htp_connp_REQ_BODY_CHUNKED_DATA_END; return HTP_OK; } } } }
htp_status_t htp_tx_req_process_body_data(htp_tx_t *tx, const void *data, size_t len) { // Keep track of the body length. tx->request_entity_len += len; // Send data to the callbacks. htp_tx_data_t d; d.tx = tx; d.data = (unsigned char *) data; d.len = len; int rc = htp_req_run_hook_body_data(tx->connp, &d); if (rc != HTP_OK) { htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request body data callback returned error (%d)", rc); return HTP_ERROR; } return HTP_OK; }
htp_status_t htp_tx_req_process_body_data_ex(htp_tx_t *tx, const void *data, size_t len) { if (tx == NULL) return HTP_ERROR; // NULL data is allowed in this private function; it's // used to indicate the end of request body. // Keep track of the body length. tx->request_entity_len += len; // Send data to the callbacks. htp_tx_data_t d; d.tx = tx; d.data = (unsigned char *) data; d.len = len; htp_status_t rc = htp_req_run_hook_body_data(tx->connp, &d); if (rc != HTP_OK) { htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request body data callback returned error (%d)", rc); return HTP_ERROR; } return HTP_OK; }
/** * The idle state is invoked before and after every transaction. Consequently, * it will start a new transaction when data is available and finalise a transaction * which has been processed. * * @param connp * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed. */ int htp_connp_REQ_IDLE(htp_connp_t * connp) { // If we're here and a transaction object exists that // means we've just completed parsing a request. We need // to run the final hook and start over. if (connp->in_tx != NULL) { // Run the last REQUEST_BODY_DATA HOOK, but // only if there was a request body if (connp->in_tx->request_transfer_coding != -1) { htp_tx_data_t d; d.data = NULL; d.len = 0; d.tx = connp->in_tx; htp_req_run_hook_body_data(connp, &d); } // Run hook REQUEST int rc = hook_run_all(connp->cfg->hook_request, connp); if (rc != HOOK_OK) { switch (rc) { case HOOK_STOP: return HTP_STOP; case HOOK_ERROR: case HOOK_DECLINED: default: htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request headers callback returned error (%d)", rc); return HTP_ERROR; } } // Clean-up if (connp->put_file != NULL) { bstr_free(&connp->put_file->filename); free(connp->put_file); connp->put_file = NULL; } // Start afresh connp->in_tx = NULL; } // We want to start parsing the next request (and change // the state from IDLE) only if there's at least one // byte of data available. Otherwise we could be creating // new structures even if there's no more data on the // connection. IN_TEST_NEXT_BYTE_OR_RETURN(connp); // Detect pipelining if (list_size(connp->conn->transactions) > connp->out_next_tx_index) { connp->conn->flags |= PIPELINED_CONNECTION; } // Parsing a new request connp->in_tx = htp_tx_create(connp->cfg, CFG_SHARED, connp->conn); if (connp->in_tx == NULL) return HTP_ERROR; connp->in_tx->connp = connp; list_add(connp->conn->transactions, connp->in_tx); connp->in_content_length = -1; connp->in_body_data_left = -1; connp->in_header_line_index = -1; connp->in_header_line_counter = 0; connp->in_chunk_request_index = connp->in_chunk_count; // Run hook TRANSACTION_START int rc = hook_run_all(connp->cfg->hook_transaction_start, connp); if (rc != HOOK_OK) { switch (rc) { case HOOK_STOP: return HTP_STOP; case HOOK_ERROR: case HOOK_DECLINED: default: htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request headers callback returned error (%d)", rc); return HTP_ERROR; } } // Change state into request line parsing connp->in_state = htp_connp_REQ_LINE; connp->in_tx->progress = TX_PROGRESS_REQ_LINE; return HTP_OK; }