Files
smartdns/src/http_parse.c
2022-07-10 10:32:01 +08:00

463 lines
9.3 KiB
C

/*************************************************************************
*
* Copyright (C) 2018-2020 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "http_parse.h"
#include "hash.h"
#include "hashtable.h"
#include "util.h"
#include "jhash.h"
#include "list.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct http_head_fields {
struct hlist_node node;
struct list_head list;
char *name;
char *value;
};
struct http_head {
HTTP_HEAD_TYPE head_type;
HTTP_METHOD method;
char *url;
char *version;
int code;
char *code_msg;
int buff_size;
int buff_len;
char *buff;
int head_ok;
int head_len;
char *data;
int data_len;
int expect_data_len;
struct http_head_fields field_head;
DECLARE_HASHTABLE(field_map, 4);
};
/*
* Returns:
* >=0 - success http data len
* -1 - Incomplete request
* -2 - parse failed
*/
struct http_head *http_head_init(int buffsize)
{
struct http_head *http_head = NULL;
char *buffer = NULL;
http_head = malloc(sizeof(*http_head));
if (http_head == NULL) {
goto errout;
}
memset(http_head, 0, sizeof(*http_head));
INIT_LIST_HEAD(&http_head->field_head.list);
hash_init(http_head->field_map);
buffer = malloc(buffsize);
if (buffer == NULL) {
goto errout;
}
http_head->buff = buffer;
http_head->buff_size = buffsize;
return http_head;
errout:
if (buffer) {
free(buffer);
}
if (http_head) {
free(http_head);
}
return NULL;
}
struct http_head_fields *http_head_first_fields(struct http_head *http_head)
{
struct http_head_fields *first = NULL;
first = list_first_entry(&http_head->field_head.list, struct http_head_fields, list);
if (first->name == NULL && first->value == NULL) {
return NULL;
}
return first;
}
const char *http_head_get_fields_value(struct http_head *http_head, const char *name)
{
uint32_t key;
struct http_head_fields *filed;
key = hash_string(name);
hash_for_each_possible(http_head->field_map, filed, node, key)
{
if (strncmp(filed->name, name, 128) == 0) {
return filed->value;
}
}
return NULL;
}
struct http_head_fields *http_head_next_fields(struct http_head_fields *fields)
{
struct http_head_fields *next = NULL;
next = list_next_entry(fields, list);
if (next->name == NULL && next->value == NULL) {
return NULL;
}
return next;
}
int http_head_lookup_fields(struct http_head_fields *fields, const char **name, const char **value)
{
if (fields == NULL) {
return -1;
}
if (name) {
*name = fields->name;
}
if (value) {
*value = fields->value;
}
return 0;
}
HTTP_METHOD http_head_get_method(struct http_head *http_head)
{
return http_head->method;
}
const char *http_head_get_url(struct http_head *http_head)
{
return http_head->url;
}
const char *http_head_get_httpversion(struct http_head *http_head)
{
return http_head->version;
}
int http_head_get_httpcode(struct http_head *http_head)
{
return http_head->code;
}
char *http_head_get_httpcode_msg(struct http_head *http_head)
{
return http_head->code_msg;
}
HTTP_HEAD_TYPE http_head_get_head_type(struct http_head *http_head)
{
return http_head->head_type;
}
char *http_head_get_data(struct http_head *http_head)
{
return http_head->data;
}
int http_head_get_data_len(struct http_head *http_head)
{
return http_head->data_len;
}
static int _http_head_add_fields(struct http_head *http_head, char *name, char *value)
{
uint32_t key = 0;
struct http_head_fields *fields = NULL;
fields = malloc(sizeof(*fields));
if (fields == NULL) {
return -1;
}
memset(fields, 0, sizeof(*fields));
fields->name = name;
fields->value = value;
list_add_tail(&fields->list, &http_head->field_head.list);
key = hash_string(name);
hash_add(http_head->field_map, &fields->node, key);
return 0;
}
static int _http_head_parse_response(struct http_head *http_head, char *key, char *value)
{
char *field_start = NULL;
char *tmp_ptr = NULL;
char *ret_msg = NULL;
char *ret_code = NULL;
if (strstr(key, "HTTP/") == NULL) {
return -1;
}
for (tmp_ptr = value; *tmp_ptr != 0; tmp_ptr++) {
if (field_start == NULL) {
field_start = tmp_ptr;
}
if (*tmp_ptr != ' ') {
continue;
}
*tmp_ptr = '\0';
ret_code = field_start;
ret_msg = tmp_ptr + 1;
field_start = NULL;
break;
}
if (ret_code == NULL || ret_msg == NULL) {
return -1;
}
if (is_numeric(ret_code) != 0) {
return -1;
}
http_head->code = atol(ret_code);
http_head->code_msg = ret_msg;
http_head->version = key;
http_head->head_type = HTTP_HEAD_RESPONSE;
return 0;
}
static int _http_head_parse_request(struct http_head *http_head, char *key, char *value)
{
int method = HTTP_METHOD_INVALID;
char *url = NULL;
char *version = NULL;
char *tmp_ptr = value;
char *field_start = NULL;
if (strncmp(key, "GET", sizeof("GET")) == 0) {
method = HTTP_METHOD_GET;
} else if (strncmp(key, "POST", sizeof("POST")) == 0) {
method = HTTP_METHOD_POST;
} else if (strncmp(key, "PUT", sizeof("PUT")) == 0) {
method = HTTP_METHOD_PUT;
} else if (strncmp(key, "DELETE", sizeof("DELETE")) == 0) {
method = HTTP_METHOD_DELETE;
} else if (strncmp(key, "TRACE", sizeof("TRACE")) == 0) {
method = HTTP_METHOD_TRACE;
} else if (strncmp(key, "CONNECT", sizeof("CONNECT")) == 0) {
method = HTTP_METHOD_CONNECT;
} else {
return _http_head_parse_response(http_head, key, value);
}
for (tmp_ptr = value; *tmp_ptr != 0; tmp_ptr++) {
if (field_start == NULL) {
field_start = tmp_ptr;
}
if (*tmp_ptr == ' ') {
*tmp_ptr = '\0';
if (url == NULL) {
url = field_start;
}
field_start = NULL;
}
}
if (field_start && version == NULL) {
version = field_start;
}
http_head->method = method;
http_head->url = url;
http_head->version = version;
http_head->head_type = HTTP_HEAD_REQUEST;
return 0;
}
static int _http_head_parse(struct http_head *http_head)
{
int i = 0;
char *key = NULL;
char *value = NULL;
char *data;
int has_first_line = 0;
int inkey = 1;
int invalue = 0;
data = http_head->buff;
for (i = 0; i < http_head->head_len; i++, data++) {
if (inkey) {
if (key == NULL && *data != ' ' && *data != '\r' && *data != '\n') {
key = data;
continue;
}
if (*data == ':' || *data == ' ') {
*data = '\0';
inkey = 0;
invalue = 1;
continue;
}
}
if (invalue) {
if (value == NULL && *data != ' ') {
value = data;
continue;
}
if (*data == '\r' || *data == '\n') {
*data = '\0';
inkey = 1;
invalue = 0;
}
}
if (key && value && invalue == 0) {
if (has_first_line == 0) {
if (_http_head_parse_request(http_head, key, value) != 0) {
return -2;
}
has_first_line = 1;
} else {
if (_http_head_add_fields(http_head, key, value) != 0) {
return -2;
}
}
key = NULL;
value = NULL;
inkey = 1;
invalue = 0;
}
}
return 0;
}
int http_head_parse(struct http_head *http_head, const char *data, int data_len)
{
int i = 0;
char *buff_end = NULL;
int left_size = 0;
int process_data_len = 0;
left_size = http_head->buff_size - http_head->buff_len;
if (left_size < data_len) {
return -3;
}
buff_end = http_head->buff + http_head->buff_len;
if (http_head->head_ok == 0) {
for (i = 0; i < data_len; i++, data++) {
*(buff_end + i) = *data;
if (*data == '\n') {
if (http_head->buff_len + i < 2) {
continue;
}
if (*(buff_end + i - 2) == '\n') {
http_head->head_ok = 1;
http_head->head_len = http_head->buff_len + i - 2;
i++;
buff_end += i;
data_len -= i;
data++;
if (_http_head_parse(http_head) != 0) {
return -2;
}
const char *content_len = NULL;
content_len = http_head_get_fields_value(http_head, "Content-Length");
if (content_len) {
http_head->expect_data_len = atol(content_len);
} else {
http_head->expect_data_len = 0;
}
if (http_head->expect_data_len < 0) {
return -2;
}
break;
}
}
}
process_data_len += i;
if (http_head->head_ok == 0) {
// Read data again */
http_head->buff_len += process_data_len;
return -1;
}
}
if (http_head->head_ok == 1) {
int get_data_len = (http_head->expect_data_len > data_len) ? data_len : http_head->expect_data_len;
if (http_head->data == NULL) {
http_head->data = buff_end;
}
memcpy(buff_end, data, get_data_len);
process_data_len += get_data_len;
http_head->data_len += get_data_len;
}
http_head->buff_len += process_data_len;
if (http_head->data_len < http_head->expect_data_len) {
return -1;
}
return process_data_len;
}
void http_head_destroy(struct http_head *http_head)
{
struct http_head_fields *fields, *tmp;
list_for_each_entry_safe(fields, tmp, &http_head->field_head.list, list)
{
list_del(&fields->list);
free(fields);
}
if (http_head->buff) {
free(http_head->buff);
}
free(http_head);
}