int htp_connp_req_data(htp_connp_t *connp, const htp_time_t *timestamp, const void *data, size_t len) { #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data(connp->in_status %x)\n", connp->in_status); fprint_raw_data(stderr, __FUNCTION__, data, len); #endif // Return if the connection is in stop state. if (connp->in_status == HTP_STREAM_STOP) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_INFO, 0, "Inbound parser is in HTP_STREAM_STOP"); return HTP_STREAM_STOP; } // Return if the connection had a fatal error earlier if (connp->in_status == HTP_STREAM_ERROR) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Inbound parser is in HTP_STREAM_ERROR"); #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (previous error)\n"); #endif return HTP_STREAM_ERROR; } // If the length of the supplied data chunk is zero, proceed // only if the stream has been closed. We do not allow zero-sized // chunks in the API, but we use them internally to force the parsers // to finalize parsing. if (((data == NULL)||(len == 0)) && (connp->in_status != HTP_STREAM_CLOSED)) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Zero-length data chunks are not allowed"); #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (zero-length chunk)\n"); #endif return HTP_STREAM_CLOSED; } // Remember the timestamp of the current request data chunk if (timestamp != NULL) { memcpy(&connp->in_timestamp, timestamp, sizeof (*timestamp)); } // Store the current chunk information connp->in_current_data = (unsigned char *) data; connp->in_current_len = len; connp->in_current_read_offset = 0; connp->in_current_consume_offset = 0; connp->in_current_receiver_offset = 0; connp->in_chunk_count++; htp_conn_track_inbound_data(connp->conn, len, timestamp); // Return without processing any data if the stream is in tunneling // mode (which it would be after an initial CONNECT transaction). if (connp->in_status == HTP_STREAM_TUNNEL) { #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_TUNNEL\n"); #endif return HTP_STREAM_TUNNEL; } if (connp->out_status == HTP_STREAM_DATA_OTHER) { connp->out_status = HTP_STREAM_DATA; } // Invoke a processor, in a loop, until an error // occurs or until we run out of data. Many processors // will process a request, each pointing to the next // processor that needs to run. for (;;) { #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: in state=%s, progress=%s\n", htp_connp_in_state_as_string(connp), htp_tx_request_progress_as_string(connp->in_tx)); #endif // Return if there's been an error or if we've run out of data. We are relying // on processors to supply error messages, so we'll keep quiet here. htp_status_t rc = connp->in_state(connp); if (rc == HTP_OK) { if (connp->in_status == HTP_STREAM_TUNNEL) { #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_TUNNEL\n"); #endif return HTP_STREAM_TUNNEL; } rc = htp_req_handle_state_change(connp); } if (rc != HTP_OK) { // Do we need more data? if ((rc == HTP_DATA) || (rc == HTP_DATA_BUFFER)) { htp_connp_req_receiver_send_data(connp, 0 /* not last */); if (rc == HTP_DATA_BUFFER) { htp_connp_req_buffer(connp); } #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA\n"); #endif connp->in_status = HTP_STREAM_DATA; return HTP_STREAM_DATA; } // Check for suspended parsing. if (rc == HTP_DATA_OTHER) { // We might have actually consumed the entire data chunk? if (connp->in_current_read_offset >= connp->in_current_len) { // Do not send STREAM_DATE_DATA_OTHER if we've consumed the entire chunk. #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (suspended parsing)\n"); #endif connp->in_status = HTP_STREAM_DATA; return HTP_STREAM_DATA; } else { // Partial chunk consumption. #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA_OTHER\n"); #endif connp->in_status = HTP_STREAM_DATA_OTHER; return HTP_STREAM_DATA_OTHER; } } // Check for the stop signal. if (rc == HTP_STOP) { #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_STOP\n"); #endif connp->in_status = HTP_STREAM_STOP; return HTP_STREAM_STOP; } #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_ERROR\n"); #endif // Permanent stream error. connp->in_status = HTP_STREAM_ERROR; return HTP_STREAM_ERROR; } } // Permanent stream error. connp->in_status = HTP_STREAM_ERROR; return HTP_STREAM_ERROR; }
/** * Process a chunk of inbound (client or request) data. * * @param connp * @param timestamp * @param data * @param len * @return HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed. */ int htp_connp_req_data(htp_connp_t *connp, htp_time_t timestamp, unsigned char *data, size_t len) { #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data(connp->in_status %x)\n", connp->in_status); fprint_raw_data(stderr, __FUNCTION__, data, len); #endif // Return if the connection has had a fatal error if (connp->in_status == STREAM_STATE_ERROR) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Inbound parser is in STREAM_STATE_ERROR"); #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning STREAM_STATE_DATA (previous error)\n"); #endif return STREAM_STATE_ERROR; } // If the length of the supplied data chunk is zero, proceed // only if the stream has been closed. We do not allow zero-sized // chunks in the API, but we use it internally to force the parsers // to finalize parsing. if ((len == 0) && (connp->in_status != STREAM_STATE_CLOSED)) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Zero-length data chunks are not allowed"); #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning STREAM_STATE_DATA (zero-length chunk)\n"); #endif return STREAM_STATE_ERROR; } // Store the current chunk information connp->in_timestamp = timestamp; connp->in_current_data = data; connp->in_current_len = len; connp->in_current_offset = 0; connp->in_chunk_count++; connp->conn->in_data_counter += len; connp->conn->in_packet_counter++; // Return without processing any data if the stream is in tunneling // mode (which it would be after an initial CONNECT transaction). if (connp->in_status == STREAM_STATE_TUNNEL) { #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning STREAM_STATE_TUNNEL\n"); #endif return STREAM_STATE_TUNNEL; } // Invoke a processor, in a loop, until an error // occurs or until we run out of data. Many processors // will process a request, each pointing to the next // processor that needs to run. for (;;) { #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: in state=%s, progress=%s\n", htp_connp_in_state_as_string(connp), htp_tx_progress_as_string(connp->in_tx, 0)); #endif // Return if there's been an error // or if we've run out of data. We are relying // on processors to add error messages, so we'll // keep quiet here. int rc = connp->in_state(connp); if (rc == HTP_OK) { if (connp->in_status == STREAM_STATE_TUNNEL) { #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning STREAM_STATE_TUNNEL\n"); #endif return STREAM_STATE_TUNNEL; } } else { // Do we need more data? if (rc == HTP_DATA) { #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning STREAM_STATE_DATA\n"); #endif return STREAM_STATE_DATA; } // Check for suspended parsing if (rc == HTP_DATA_OTHER) { // We might have actually consumed the entire data chunk? if (connp->in_current_offset >= connp->in_current_len) { // Do not send STREAM_DATE_DATA_OTHER if we've // consumed the entire chunk #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning STREAM_STATE_DATA (suspended parsing)\n"); #endif return STREAM_STATE_DATA; } else { // Partial chunk consumption #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning STREAM_STATE_DATA_OTHER\n"); #endif return STREAM_STATE_DATA_OTHER; } } // Remember that we've had an error. Errors are // (at least at present) not possible to recover from. connp->in_status = STREAM_STATE_ERROR; #ifdef HTP_DEBUG fprintf(stderr, "htp_connp_req_data: returning STREAM_STATE_ERROR (state response)\n"); #endif return STREAM_STATE_ERROR; } } return HTP_ERROR; }