diff options
Diffstat (limited to 'src/protocol_decoder/http/http_entry.c')
| -rw-r--r-- | src/protocol_decoder/http/http_entry.c | 677 |
1 files changed, 677 insertions, 0 deletions
diff --git a/src/protocol_decoder/http/http_entry.c b/src/protocol_decoder/http/http_entry.c new file mode 100644 index 0000000..05be602 --- /dev/null +++ b/src/protocol_decoder/http/http_entry.c @@ -0,0 +1,677 @@ +#include <assert.h> +#include <string.h> + +#include "http_entry.h" +#include "http_decoder_util.h" +#include "http_decoder_half.h" +#include "http_decoder_table.h" + +#define HTTP_DECODER_RESULT_QUEUE_SIZE 16 + +#define HTTP_DECODER_IS_CACHE_LINE 1 +#define HTTP_DECODER_IS_CACHE_BODY 1 +#define HTTP_DECODER_IS_CACHE_HEADER 1 + +#define HTTP_DECODER_CONTEXT "HTTP_DECODER_CONTEXT" +#define HTTP_DECODER_RESULT "HTTP_DECODER_RESULT" + +size_t http_decoder_context_index = session_register_ex_data_index(HTTP_DECODER_CONTEXT); +size_t http_decoder_result_index = session_register_ex_data_index(HTTP_DECODER_RESULT); + +/****************************************************************************** + * struct define + ******************************************************************************/ + +// per http session +struct http_decoder_result +{ + struct stellar_session *http_session; // pointer reference + + struct http_decoder_half_data *req_data; + struct http_decoder_half_data *resp_data; + + const char *close_reason; +}; + +// per tcp session +struct http_decoder +{ + struct http_decoder_half *c2s_half; + struct http_decoder_half *s2c_half; +}; + +struct http_decoder_result_queue +{ + size_t del_index; + size_t req_index; + size_t resp_index; + size_t queue_size; + struct http_decoder_result **array; +}; + +struct http_decoder_context +{ + struct stellar_session *tcp_session; // pointer reference + + struct http_decoder *decoder; + struct http_decoder_result_queue *decoder_result_queue; + + int current_packet_have_trigger_req_hdr_event; + int current_packet_have_trigger_resp_hdr_event; +}; + +static void http_event_handler(enum http_event event, struct http_decoder_half_data **data, void *cb_args); + +static inline void http_decoder_context_free_cb(void *data, void *cb_arg); +static inline void http_decoder_result_free_cb(void *data, void *cb_arg); + +/****************************************************************************** + * http_decoder_result API + ******************************************************************************/ + +struct http_decoder_result *http_decoder_result_create() +{ + struct http_decoder_result *result = safe_alloc(struct http_decoder_result, 1); + assert(result); + + result->http_session = NULL; + result->close_reason = NULL; + result->req_data = NULL; + result->resp_data = NULL; + + // create half data on use + // result->req_data = http_decoder_half_data_create(); + // result->resp_data = http_decoder_half_data_create(); + // assert(result->req_data); + // assert(result->resp_data); + + return result; +} + +void http_decoder_result_destory(struct http_decoder_result *result) +{ + if (result) + { + http_decoder_half_data_destory(result->req_data); + http_decoder_half_data_destory(result->resp_data); + + result->http_session = NULL; + result->close_reason = NULL; + + safe_free(result); + } +} + +/****************************************************************************** + * http_decoder_result_queue API + ******************************************************************************/ + +static struct http_decoder_result_queue *http_decoder_result_queue_create(size_t queue_size) +{ + struct http_decoder_result_queue *queue = safe_alloc(struct http_decoder_result_queue, 1); + assert(queue); + + queue->del_index = 0; + queue->req_index = 0; + queue->resp_index = 0; + queue->queue_size = queue_size; + + queue->array = safe_alloc(struct http_decoder_result *, queue->queue_size); + assert(queue->array); + + return queue; +} + +static void http_decoder_result_queue_destory(struct http_decoder_result_queue *queue) +{ + if (queue) + { + if (queue->array) + { + for (size_t i = 0; i < queue->queue_size; i++) + { + struct http_decoder_result *result = queue->array[i]; + if (result) + { + http_decoder_result_destory(result); + } + } + safe_free(queue->array); + } + + queue->del_index = 0; + queue->req_index = 0; + queue->resp_index = 0; + queue->queue_size = 0; + + safe_free(queue); + } +} + +static void http_decoder_result_queue_reinit(struct http_decoder_result_queue *queue) +{ + assert(queue); + + queue->del_index = 0; + queue->req_index = 0; + queue->resp_index = 0; + // queue->queue_size; + + for (size_t i = 0; i < queue->queue_size; i++) + { + struct http_decoder_result *result = queue->array[i]; + if (result) + { + http_decoder_result_destory(result); + } + } + + struct http_decoder_result **temp = queue->array; + memset(queue->array, 0, sizeof(struct http_decoder_result *) * queue->queue_size); + queue->array = temp; +} + +static void http_decoder_result_queue_inc_req_index(struct http_decoder_result_queue *queue) +{ + assert(queue); + + queue->req_index++; + queue->req_index = queue->req_index % queue->queue_size; +} + +static void http_decoder_result_queue_inc_resp_index(struct http_decoder_result_queue *queue) +{ + assert(queue); + + queue->resp_index++; + queue->resp_index = queue->resp_index % queue->queue_size; +} + +static void http_decoder_result_queue_inc_del_index(struct http_decoder_result_queue *queue) +{ + assert(queue); + + queue->del_index++; + queue->del_index = queue->del_index % queue->queue_size; +} + +// Create a new http result and add it to the queue +static void http_decoder_result_queue_push(struct http_decoder_result_queue *queue, struct stellar_session *tcp_session, size_t index) +{ + assert(queue); + assert(index >= 0 && index < queue->queue_size); + + if (queue->array[index] == NULL) + { + queue->array[index] = http_decoder_result_create(); + assert(queue->array[index]); + + queue->array[index]->http_session = session_derive(tcp_session, "HTTP"); + assert(queue->array[index]->http_session); + } + + if (session_get_ex_data(queue->array[index]->http_session, http_decoder_result_index) == NULL) + { + session_set_ex_data(queue->array[index]->http_session, http_decoder_result_index, queue->array[index], http_decoder_result_free_cb, NULL); + } +} + +// Remove the http result from the queue but not destroy it +static void http_decoder_result_queue_pop(struct http_decoder_result_queue *queue, size_t index, const char *close_reason) +{ + assert(queue); + assert(index >= 0 && index < queue->queue_size); + + if (queue->array[index]) + { + queue->array[index]->close_reason = close_reason; + session_trigger(queue->array[index]->http_session, "http.closing"); + queue->array[index] = NULL; + } +} + +// Remove old http result if queue is full +static void http_decoder_result_queue_gc(struct http_decoder_result_queue *queue, size_t index, const char *close_reason) +{ + assert(queue); + assert(index >= 0 && index < queue->queue_size); + + if (index == queue->del_index) + { + http_decoder_result_queue_pop(queue, index, close_reason); + http_decoder_result_queue_inc_del_index(queue); + } +} + +static struct stellar_session *http_decoder_result_queue_get_session(struct http_decoder_result_queue *queue, size_t index) +{ + assert(queue); + assert(index >= 0 && index < queue->queue_size); + + return (queue->array[index] ? queue->array[index]->http_session : NULL); +} + +/****************************************************************************** + * http_decoder API + ******************************************************************************/ + +static struct http_decoder *http_decoder_create() +{ + struct http_decoder *decoder = safe_alloc(struct http_decoder, 1); + assert(decoder); + + decoder->c2s_half = http_decoder_half_create(); + decoder->s2c_half = http_decoder_half_create(); + assert(decoder->c2s_half); + assert(decoder->s2c_half); + + return decoder; +} + +static void http_decoder_destory(struct http_decoder *decoder) +{ + if (decoder) + { + http_decoder_half_destory(decoder->c2s_half); + http_decoder_half_destory(decoder->s2c_half); + + safe_free(decoder); + } +} + +static void http_decoder_reinit(struct http_decoder *decoder, void *args) +{ + assert(decoder); + + http_decoder_half_init(decoder->c2s_half, http_event_handler, args, HTTP_DECODER_IS_CACHE_LINE, HTTP_DECODER_IS_CACHE_HEADER, HTTP_DECODER_IS_CACHE_BODY); + http_decoder_half_init(decoder->s2c_half, http_event_handler, args, HTTP_DECODER_IS_CACHE_LINE, HTTP_DECODER_IS_CACHE_HEADER, HTTP_DECODER_IS_CACHE_BODY); +} + +/****************************************************************************** + * http_decoder_context API + ******************************************************************************/ + +static struct http_decoder_context *http_decoder_context_create() +{ + struct http_decoder_context *ctx = safe_alloc(struct http_decoder_context, 1); + assert(ctx); + + ctx->decoder = http_decoder_create(); + assert(ctx->decoder); + + ctx->decoder_result_queue = http_decoder_result_queue_create(HTTP_DECODER_RESULT_QUEUE_SIZE); + assert(ctx->decoder_result_queue); + + ctx->tcp_session = NULL; + + return ctx; +} + +static void http_decoder_context_destory(struct http_decoder_context *ctx) +{ + if (ctx) + { + http_decoder_result_queue_destory(ctx->decoder_result_queue); + http_decoder_destory(ctx->decoder); + + safe_free(ctx); + } +} + +static void http_decoder_context_reinit(struct http_decoder_context *ctx) +{ + assert(ctx); + + http_decoder_reinit(ctx->decoder, ctx); + http_decoder_result_queue_reinit(ctx->decoder_result_queue); +} + +/****************************************************************************** + * free cb + ******************************************************************************/ + +static inline void http_decoder_context_free_cb(void *data, void *cb_arg) +{ + http_decoder_context_destory((struct http_decoder_context *)data); +} + +static inline void http_decoder_result_free_cb(void *data, void *cb_arg) +{ + http_decoder_result_destory((struct http_decoder_result *)data); +} + +/****************************************************************************** + * http_decoder_entry + ******************************************************************************/ + +/* + * +-------+-------+-------+ + * | req1 | req2 | req3 | + * +-------+-------+-------+ + * | resp1 | resp2 | resp3 | + * +-------+-------+-------+ + * + * case 1: req == null && resp == null + * case 2: req == null && resp == part + * case 3: req == null && resp == full + * + * case 4: req == part && resp == null + * case 5: req == part && resp == part + * case 6: req == part && resp == full + * + * case 7: req == full && resp == null + * case 8: req == full && resp == part + * case 9: req == full && resp == full + */ + +static void http_event_handler(enum http_event event, struct http_decoder_half_data **data, void *cb_args) +{ + struct http_decoder_context *ctx = (struct http_decoder_context *)cb_args; + assert(ctx); + + struct http_decoder_result_queue *queue = ctx->decoder_result_queue; + assert(queue); + + // printf("event => %s\n", http_event_int2str(event)); + // http_decoder_half_data_dump(*data); + + struct stellar_session *req_session = http_decoder_result_queue_get_session(queue, queue->req_index); + struct stellar_session *resp_session = http_decoder_result_queue_get_session(queue, queue->resp_index); + + switch (event) + { + case HTTP_EVENT_INIT: + *data = http_decoder_half_data_create(); + break; + case HTTP_EVENT_REQ_LINE: + if (req_session == NULL) + { + http_decoder_result_queue_push(queue, ctx->tcp_session, queue->req_index); + req_session = http_decoder_result_queue_get_session(queue, queue->req_index); + } + queue->array[queue->req_index]->req_data = *data; + session_trigger(req_session, "http.opening"); + session_trigger(req_session, "http.req_line"); + break; + case HTTP_EVENT_REQ_HDR: + if (ctx->current_packet_have_trigger_req_hdr_event == 0) + { + session_trigger(req_session, "http.req_hdr"); + ctx->current_packet_have_trigger_req_hdr_event = 1; + } + break; + case HTTP_EVENT_REQ_HDR_END: + session_trigger(req_session, "http.req_hdr_end"); + break; + case HTTP_EVENT_REQ_BODY_BEGIN: + session_trigger(req_session, "http.req_body_begin"); + break; + case HTTP_EVENT_REQ_BODY_DATA: + session_trigger(req_session, "http.req_body_data"); + break; + case HTTP_EVENT_REQ_BODY_END: + session_trigger(req_session, "http.req_body_end"); + break; + case HTTP_EVENT_REQ_END: + session_trigger(req_session, "http.req_end"); + + http_decoder_result_queue_inc_req_index(queue); + http_decoder_result_queue_gc(queue, queue->req_index, "req queue full"); + *data = NULL; + break; + case HTTP_EVENT_RESP_LINE: + // maybe only S2C traffic + if (resp_session == NULL) + { + http_decoder_result_queue_push(queue, ctx->tcp_session, queue->resp_index); + resp_session = http_decoder_result_queue_get_session(queue, queue->resp_index); + + session_trigger(resp_session, "http.opening"); + } + queue->array[queue->resp_index]->req_data = *data; + session_trigger(resp_session, "http.resp_line"); + break; + case HTTP_EVENT_RESP_HDR: + if (ctx->current_packet_have_trigger_resp_hdr_event == 0) + { + session_trigger(resp_session, "http.resp_hdr"); + ctx->current_packet_have_trigger_resp_hdr_event = 1; + } + break; + case HTTP_EVENT_RESP_HDR_END: + session_trigger(resp_session, "http.resp_hdr_end"); + break; + case HTTP_EVENT_RESP_BODY_BEGIN: + session_trigger(resp_session, "http.resp_body_begin"); + break; + case HTTP_EVENT_RESP_BODY_DATA: + session_trigger(resp_session, "http.resp_body_data"); + break; + case HTTP_EVENT_RESP_BODY_END: + session_trigger(resp_session, "http.resp_body_end"); + break; + case HTTP_EVENT_RESP_END: + session_trigger(resp_session, "http.resp_end"); + session_trigger(resp_session, "http.closing"); + + http_decoder_result_queue_inc_resp_index(queue); + http_decoder_result_queue_gc(queue, queue->resp_index, "resp queue full"); + *data = NULL; + break; + default: + assert(0); + break; + } +} + +void http_entry(struct stellar_session *tcp_session, const char *tcp_event, int thread_id) +{ + uint8_t tcp_dir = session_get_l4_dir(tcp_session); + const char *payload = session_get_l4_payload(tcp_session); + size_t payload_len = session_get_l4_payload_length(tcp_session); + struct http_decoder_context *ctx = (struct http_decoder_context *)session_get_ex_data(tcp_session, http_decoder_context_index); + + if (strncmp("tcp.opening", tcp_event, strlen(tcp_event)) == 0) + { + // nothing + } + + if (strncmp("tcp.order_pkt", tcp_event, strlen(tcp_event)) == 0) + { + // fist order pakcet on this tcp session + if (ctx == NULL) + { + // not http protocol + if (http_decoder_half_identify(payload, (payload_len > 16 ? 16 : payload_len)) == -1) + { + pm_session_dettach_me(tcp_session); + return; + } + // is http protocol + else + { + ctx = http_decoder_context_create(); + ctx->tcp_session = tcp_session; + http_decoder_context_reinit(ctx); + session_set_ex_data(ctx->tcp_session, http_decoder_context_index, ctx, http_decoder_context_free_cb, NULL); + } + } + ctx->current_packet_have_trigger_req_hdr_event = 0; + ctx->current_packet_have_trigger_resp_hdr_event = 0; + + struct http_decoder_result_queue *queue = ctx->decoder_result_queue; + assert(queue); + struct http_decoder_half *current_half = NULL; + if (tcp_dir == SESSION_DIR_C2S) + { + current_half = ctx->decoder->c2s_half; + } + else + { + current_half = ctx->decoder->s2c_half; + } + + if (http_decoder_half_parse(current_half, payload, payload_len) == -1) + { + if (tcp_dir == SESSION_DIR_C2S) + { + http_decoder_result_queue_pop(queue, queue->req_index, "c2s missing fragment"); + } + else + { + http_decoder_result_queue_pop(queue, queue->resp_index, "c2s missing fragment"); + } + + const char *start_ptr = payload; + const char *end_ptr = payload + payload_len; + const char *target = NULL; + while (start_ptr < end_ptr && (target = strstr(start_ptr, "\r\n\r\n")) != NULL) + { + start_ptr = target + 4; + if (start_ptr < end_ptr && http_decoder_half_identify(start_ptr, end_ptr - start_ptr) == 0) + { + http_decoder_half_init(current_half, http_event_handler, ctx, HTTP_DECODER_IS_CACHE_LINE, HTTP_DECODER_IS_CACHE_HEADER, HTTP_DECODER_IS_CACHE_BODY); + http_decoder_half_parse(current_half, start_ptr, end_ptr - start_ptr); + break; + } + else + { + continue; + } + }; + } + } + + if (strncmp("tcp.closing", tcp_event, strlen(tcp_event)) == 0) + { + if (ctx) + { + struct http_decoder_result_queue *queue = ctx->decoder_result_queue; + assert(queue); + + // trigger http closing event on all sub http session + for (size_t i = 0; i < queue->queue_size; i++) + { + http_decoder_result_queue_pop(queue, i, "tcp closing"); + } + + // free tcp session ctx + session_del_ex_data(ctx->tcp_session, http_decoder_context_index); + http_decoder_context_destory(ctx); + } + } +} + +/****************************************************************************** + * Public API: For HTTP consumer + ******************************************************************************/ + +void http_session_get_request_line(struct stellar_session *http_session, struct http_request_line *line) +{ + struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); + assert(result); + assert(line); + + http_decoder_table_get_method(result->req_data->table, (struct rstring *)&line->method); + http_decoder_table_get_uri(result->req_data->table, (struct rstring *)&line->uri); + http_decoder_table_get_version(result->req_data->table, (struct rstring *)&line->version); + + line->major_version = result->req_data->major_version; + line->minor_version = result->req_data->minor_version; +} + +void http_session_get_response_line(struct stellar_session *http_session, struct http_response_line *line) +{ + struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); + assert(result); + assert(line); + + http_decoder_table_get_version(result->resp_data->table, (struct rstring *)&line->version); + http_decoder_table_get_status(result->resp_data->table, (struct rstring *)&line->status); + + line->major_version = result->resp_data->major_version; + line->minor_version = result->resp_data->minor_version; + line->status_code = result->resp_data->status_code; +} + +void http_session_get_request_raw_body(struct stellar_session *http_session, struct http_string *body) +{ + struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); + assert(result); + assert(body); + + http_decoder_table_get_body(result->req_data->table, (struct rstring *)body); +} + +void http_session_get_response_raw_body(struct stellar_session *http_session, struct http_string *body) +{ + struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); + assert(result); + assert(body); + + http_decoder_table_get_body(result->resp_data->table, (struct rstring *)body); +} + +void http_session_get_request_decompress_body(struct stellar_session *http_session, struct http_string *body) +{ + struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); + assert(result); + assert(body); + + body->str = result->req_data->decompress_body; + body->len = result->req_data->decompress_body_len; +} + +void http_session_get_response_decompress_body(struct stellar_session *http_session, struct http_string *body) +{ + struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); + assert(result); + assert(body); + + body->str = result->resp_data->decompress_body; + body->len = result->resp_data->decompress_body_len; +} + +size_t http_session_get_request_header(struct stellar_session *http_session, struct http_string *header_key, struct http_string header_val[], size_t val_size) +{ + struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); + assert(result); + assert(header_key); + assert(header_val); + assert(val_size); + + return http_decoder_table_get_header(result->req_data->table, (struct rstring *)header_key, (struct rstring *)header_val, val_size); +} + +size_t http_session_get_response_header(struct stellar_session *http_session, struct http_string *header_key, struct http_string header_val[], size_t val_size) +{ + struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); + assert(result); + assert(header_key); + assert(header_val); + assert(val_size); + + return http_decoder_table_get_header(result->resp_data->table, (struct rstring *)header_key, (struct rstring *)header_val, val_size); +} + +void http_session_iter_request_header(struct stellar_session *http_session, size_t *iter_index, struct http_string *header_key, struct http_string *header_val) +{ + struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); + assert(result); + assert(iter_index); + assert(header_key); + assert(header_val); + + http_decoder_table_iter_header(result->req_data->table, iter_index, (struct rstring *)header_key, (struct rstring *)header_val); +} + +void http_session_iter_response_header(struct stellar_session *http_session, size_t *iter_index, struct http_string *header_key, struct http_string *header_val) +{ + struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); + assert(result); + assert(iter_index); + assert(header_key); + assert(header_val); + + http_decoder_table_iter_header(result->resp_data->table, iter_index, (struct rstring *)header_key, (struct rstring *)header_val); +} |
