From 4e4ae88f0e2093f0a354e396169694d40eb08d6d Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Wed, 8 Jan 2014 17:32:24 +0000 Subject: [PATCH] Implement http server using http_parser. --- CMakeLists.txt | 4 +- lib/CMakeLists.txt | 2 + src/http.c | 591 +++++++++++++++++++++++++++++++++++++++++++++ src/http.h | 164 +++++++++++++ src/lua/lua_http.c | 3 +- src/map.c | 3 +- src/util.c | 268 -------------------- src/util.h | 8 - 8 files changed, 764 insertions(+), 279 deletions(-) create mode 100644 src/http.c create mode 100644 src/http.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ffbc24974..b3038ba5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -875,7 +875,8 @@ ENDIF(HG) INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src" "${CMAKE_BINARY_DIR}/src" "${CMAKE_SOURCE_DIR}/src/ucl/include" - "${CMAKE_SOURCE_DIR}/contrib/uthash") + "${CMAKE_SOURCE_DIR}/contrib/uthash" + "${CMAKE_SOURCE_DIR}/contrib/http-parser") SET(RSPAMDSRC src/modules.c src/controller.c @@ -917,6 +918,7 @@ ADD_SUBDIRECTORY(src/lua) ADD_SUBDIRECTORY(src/json) ADD_SUBDIRECTORY(src/cdb) ADD_SUBDIRECTORY(src/ucl) +ADD_SUBDIRECTORY(contrib/http-parser) ADD_SUBDIRECTORY(lib) ADD_SUBDIRECTORY(src/client) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b236be14a..0f3cba7b4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -8,6 +8,7 @@ SET(LIBRSPAMDUTILSRC ../src/aio_event.c ../src/fstring.c ../src/fuzzy.c ../src/hash.c + ../src/http.c ../src/logger.c ../src/map.c ../src/memcached.c @@ -73,6 +74,7 @@ ENDIF(CMAKE_COMPILER_IS_GNUCC) TARGET_LINK_LIBRARIES(rspamd-util ${RSPAMD_REQUIRED_LIBRARIES}) TARGET_LINK_LIBRARIES(rspamd-util pcre) TARGET_LINK_LIBRARIES(rspamd-util rspamd-ucl) +TARGET_LINK_LIBRARIES(rspamd-util rspamd-http-parser) TARGET_LINK_LIBRARIES(rspamd-util event) IF(NOT DEBIAN_BUILD) diff --git a/src/http.c b/src/http.c new file mode 100644 index 000000000..4d036428b --- /dev/null +++ b/src/http.c @@ -0,0 +1,591 @@ +/* Copyright (c) 2014, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "http.h" +#include "utlist.h" + +struct rspamd_http_server_private { + GString *buf; + gboolean new_header; + struct rspamd_http_header *header; + struct http_parser parser; + struct http_parser_settings parser_cb; + struct event ev; + struct timeval tv; + struct timeval *ptv; + gboolean in_body; + struct rspamd_http_request *req; +}; + +#define HTTP_ERROR http_error_quark () +GQuark +http_error_quark (void) +{ + return g_quark_from_static_string ("http-error-quark"); +} + +static inline void +rspamd_http_check_date (struct rspamd_http_server_private *priv) +{ + if (g_ascii_strcasecmp (priv->header->name->str, "date") == 0) { + priv->req->date = rspamd_http_parse_date (priv->header->value->str, + priv->header->value->len); + } +} + +static gint +rspamd_http_on_url (http_parser* parser, const gchar *at, size_t length) +{ + struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data; + struct rspamd_http_server_private *priv; + + priv = serv->priv; + + g_string_append_len (priv->req->url, at, length); + + return 0; +} + +static gint +rspamd_http_on_header_field (http_parser* parser, const gchar *at, size_t length) +{ + struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data; + struct rspamd_http_server_private *priv; + + priv = serv->priv; + + if (priv->header == NULL) { + priv->header = g_slice_alloc (sizeof (struct rspamd_http_header)); + priv->header->name = g_string_sized_new (32); + priv->header->value = g_string_sized_new (32); + } + else if (priv->new_header) { + LL_PREPEND (priv->req->headers, priv->header); + rspamd_http_check_date (priv); + priv->header = g_slice_alloc (sizeof (struct rspamd_http_header)); + priv->header->name = g_string_sized_new (32); + priv->header->value = g_string_sized_new (32); + } + + priv->new_header = FALSE; + g_string_append_len (priv->header->name, at, length); + + return 0; +} + +static gint +rspamd_http_on_header_value (http_parser* parser, const gchar *at, size_t length) +{ + struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data; + struct rspamd_http_server_private *priv; + + priv = serv->priv; + + if (priv->header == NULL) { + /* Should not happen */ + return -1; + } + + priv->new_header = TRUE; + g_string_append_len (priv->header->value, at, length); + + return 0; +} + +static int +rspamd_http_on_headers_complete (http_parser* parser) +{ + struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data; + struct rspamd_http_server_private *priv; + + priv = serv->priv; + + if (priv->header != NULL) { + LL_PREPEND (priv->req->headers, priv->header); + rspamd_http_check_date (priv); + priv->header = NULL; + } + + priv->in_body = TRUE; + if (parser->content_length != 0 && parser->content_length != ULLONG_MAX) { + priv->req->body = g_string_sized_new (parser->content_length); + } + else { + priv->req->body = g_string_sized_new (BUFSIZ); + } + + return 0; +} + +static int +rspamd_http_on_body (http_parser* parser, const gchar *at, size_t length) +{ + struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data; + + if (serv->opts & RSPAMD_HTTP_BODY_PARTIAL) { + return (serv->body_handler (serv, serv->priv->req, at, length)); + } + + return 0; +} + +static int +rspamd_http_on_message_complete (http_parser* parser) +{ + struct rspamd_http_server *serv = (struct rspamd_http_server *)parser->data; + struct rspamd_http_server_private *priv; + int ret; + + priv = serv->priv; + + if (serv->opts & RSPAMD_HTTP_BODY_PARTIAL) { + ret = serv->body_handler (serv, priv->req, NULL, 0); + } + else { + ret = serv->body_handler (serv, priv->req, priv->req->body->str, priv->req->body->len); + } + + return ret; +} + +static void +rspamd_http_event_handler (int fd, short what, gpointer ud) +{ + struct rspamd_http_server *serv = (struct rspamd_http_server *)ud; + struct rspamd_http_server_private *priv; + GString *buf; + gchar *start; + gssize r; + gint64 remain; + GError *err; + + priv = serv->priv; + if (priv->in_body) { + buf = priv->req->body; + } + else { + priv->buf->len = 0; + buf = priv->buf; + } + + remain = buf->allocated_len - buf->len; + if (remain <= 0) { + /* Expand string */ + g_string_set_size (buf, buf->allocated_len * 2); + remain = buf->allocated_len - buf->len; + } + start = buf->str + buf->len; + r = read (fd, start, remain); + if (r == -1) { + err = g_error_new (HTTP_ERROR, errno, "IO read error: %s", strerror (errno)); + serv->error_handler (serv, err); + g_error_free (err); + } + else { + buf->len += r; + if (http_parser_execute (&priv->parser, &priv->parser_cb, start, r) != (size_t)r) { + err = g_error_new (HTTP_ERROR, priv->parser.http_errno, + "HTTP parser error: %s", http_errno_description (priv->parser.http_errno)); + serv->error_handler (serv, err); + g_error_free (err); + } + } +} + +struct rspamd_http_server* +rspamd_http_server_new (rspamd_http_body_handler body_handler, + rspamd_http_error_handler error_handler, enum rspamd_http_options opts) +{ + struct rspamd_http_server *new; + struct rspamd_http_server_private *priv; + + new = g_slice_alloc0 (sizeof (struct rspamd_http_server)); + new->opts = opts; + new->body_handler = body_handler; + new->error_handler = error_handler; + new->fd = -1; + + /* Init priv */ + priv = g_slice_alloc0 (sizeof (struct rspamd_http_server_private)); + http_parser_init (&priv->parser, HTTP_REQUEST); + priv->parser.data = new; + priv->parser_cb.on_url = rspamd_http_on_url; + priv->parser_cb.on_header_field = rspamd_http_on_header_field; + priv->parser_cb.on_header_value = rspamd_http_on_header_value; + priv->parser_cb.on_headers_complete = rspamd_http_on_headers_complete; + priv->parser_cb.on_body = rspamd_http_on_body; + priv->parser_cb.on_message_complete = rspamd_http_on_message_complete; + + new->priv = priv; + + return new; +} + +void +rspamd_http_server_reset (struct rspamd_http_server *server) +{ + struct rspamd_http_server_private *priv; + struct rspamd_http_request *req; + struct rspamd_http_header *hdr, *tmp_hdr; + + priv = server->priv; + req = priv->req; + + /* Clear request */ + if (req != NULL) { + LL_FOREACH_SAFE(req->headers, hdr, tmp_hdr) { + g_string_free (hdr->name, TRUE); + g_string_free (hdr->value, TRUE); + g_slice_free1 (sizeof (struct rspamd_http_header), hdr); + } + g_string_free (req->body, TRUE); + g_string_free (req->url, TRUE); + g_slice_free1 (sizeof (struct rspamd_http_request), req); + priv->req = NULL; + } + + /* Clear priv */ + event_del (&priv->ev); + if (priv->buf != NULL) { + g_string_free (priv->buf, TRUE); + priv->buf = NULL; + } + + /* Clear server itself */ + if (server->fd != -1) { + close (server->fd); + } +} + +void +rspamd_http_server_free (struct rspamd_http_server *server) +{ + struct rspamd_http_server_private *priv; + + priv = server->priv; + rspamd_http_server_reset (server); + g_slice_free1 (sizeof (struct rspamd_http_server_private), priv); + g_slice_free1 (sizeof (struct rspamd_http_server), server); +} + +void +rspamd_http_server_handle_request (struct rspamd_http_server *server, + gpointer ud, gint fd, struct timeval *timeout, struct event_base *base) +{ + struct rspamd_http_server_private *priv = server->priv; + struct rspamd_http_request *req; + + server->fd = fd; + server->ud = ud; + req = g_slice_alloc (sizeof (struct rspamd_http_request)); + req->url = g_string_sized_new (32); + req->headers = NULL; + req->date = 0; + priv->req = req; + + if (timeout == NULL) { + priv->ptv = NULL; + } + else { + memcpy (&priv->tv, timeout, sizeof (struct timeval)); + priv->ptv = &priv->tv; + } + priv->header = NULL; + priv->buf = g_string_sized_new (BUFSIZ); + priv->in_body = FALSE; + priv->new_header = TRUE; + + event_set (&priv->ev, fd, EV_READ | EV_PERSIST, rspamd_http_event_handler, server); + event_base_set (base, &priv->ev); + event_add (&priv->ev, priv->ptv); +} + + +/* + * Obtained from nginx + * Copyright (C) Igor Sysoev + */ +static guint mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +time_t +rspamd_http_parse_date (const gchar *header, gsize len) +{ + const gchar *p, *end; + gint month; + guint day, year, hour, min, sec; + guint64 time; + enum { + no = 0, rfc822, /* Tue, 10 Nov 2002 23:50:13 */ + rfc850, /* Tuesday, 10-Dec-02 23:50:13 */ + isoc /* Tue Dec 10 23:50:13 2002 */ + } fmt; + + fmt = 0; + if (len > 0) { + end = header + len; + } + else { + end = header + strlen (header); + } + +#if (NGX_SUPPRESS_WARN) + day = 32; + year = 2038; +#endif + + for (p = header; p < end; p++) { + if (*p == ',') { + break; + } + + if (*p == ' ') { + fmt = isoc; + break; + } + } + + for (p++; p < end; p++) + if (*p != ' ') { + break; + } + + if (end - p < 18) { + return (time_t)-1; + } + + if (fmt != isoc) { + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return (time_t)-1; + } + + day = (*p - '0') * 10 + *(p + 1) - '0'; + p += 2; + + if (*p == ' ') { + if (end - p < 18) { + return (time_t)-1; + } + fmt = rfc822; + + } + else if (*p == '-') { + fmt = rfc850; + + } + else { + return (time_t)-1; + } + + p++; + } + + switch (*p) { + + case 'J': + month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6; + break; + + case 'F': + month = 1; + break; + + case 'M': + month = *(p + 2) == 'r' ? 2 : 4; + break; + + case 'A': + month = *(p + 1) == 'p' ? 3 : 7; + break; + + case 'S': + month = 8; + break; + + case 'O': + month = 9; + break; + + case 'N': + month = 10; + break; + + case 'D': + month = 11; + break; + + default: + return (time_t)-1; + } + + p += 3; + + if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) { + return (time_t)-1; + } + + p++; + + if (fmt == rfc822) { + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9' + || *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0' + || *(p + 3) > '9') { + return (time_t)-1; + } + + year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100 + + (*(p + 2) - '0') * 10 + *(p + 3) - '0'; + p += 4; + + } + else if (fmt == rfc850) { + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return (time_t)-1; + } + + year = (*p - '0') * 10 + *(p + 1) - '0'; + year += (year < 70) ? 2000 : 1900; + p += 2; + } + + if (fmt == isoc) { + if (*p == ' ') { + p++; + } + + if (*p < '0' || *p > '9') { + return (time_t)-1; + } + + day = *p++ - '0'; + + if (*p != ' ') { + if (*p < '0' || *p > '9') { + return (time_t)-1; + } + + day = day * 10 + *p++ - '0'; + } + + if (end - p < 14) { + return (time_t)-1; + } + } + + if (*p++ != ' ') { + return (time_t)-1; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return (time_t)-1; + } + + hour = (*p - '0') * 10 + *(p + 1) - '0'; + p += 2; + + if (*p++ != ':') { + return (time_t)-1; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return (time_t)-1; + } + + min = (*p - '0') * 10 + *(p + 1) - '0'; + p += 2; + + if (*p++ != ':') { + return (time_t)-1; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return (time_t)-1; + } + + sec = (*p - '0') * 10 + *(p + 1) - '0'; + + if (fmt == isoc) { + p += 2; + + if (*p++ != ' ') { + return (time_t)-1; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9' + || *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0' + || *(p + 3) > '9') { + return (time_t)-1; + } + + year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100 + + (*(p + 2) - '0') * 10 + *(p + 3) - '0'; + } + + if (hour > 23 || min > 59 || sec > 59) { + return (time_t)-1; + } + + if (day == 29 && month == 1) { + if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) { + return (time_t)-1; + } + + } + else if (day > mday[month]) { + return (time_t)-1; + } + + /* + * shift new year to March 1 and start months from 1 (not 0), + * it is needed for Gauss' formula + */ + + if (--month <= 0) { + month += 12; + year -= 1; + } + + /* Gauss' formula for Gregorian days since March 1, 1 BC */ + + time = (guint64) ( + /* days in years including leap years since March 1, 1 BC */ + + 365 * year + year / 4 - year / 100 + year / 400 + + /* days before the month */ + + + 367 * month / 12 - 30 + + /* days before the day */ + + + day - 1 + + /* + * 719527 days were between March 1, 1 BC and March 1, 1970, + * 31 and 28 days were in January and February 1970 + */ + + - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec; + + return (time_t) time; +} diff --git a/src/http.h b/src/http.h new file mode 100644 index 000000000..8506097a5 --- /dev/null +++ b/src/http.h @@ -0,0 +1,164 @@ +/* Copyright (c) 2014, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTTP_H_ +#define HTTP_H_ + +/** + * @file http.h + * + * This is an interface for HTTP client and server. This code uses HTTP parser written + * by Joyent Inc based on nginx code. + */ + +#include "config.h" +#include "http_parser.h" + +/** + * HTTP header structure + */ +struct rspamd_http_header { + GString *name; + GString *value; + struct rspamd_http_header *next; +}; + +/** + * HTTP request structure, used for requests + */ +struct rspamd_http_request { + GString *url; + struct rspamd_http_header *headers; + GString *body; + time_t date; + gint code; +}; + +struct rspamd_http_reply { + struct rspamd_http_header *headers; + GString *body; + gint code; +}; + +/** + * Options for HTTP client and server + */ +enum rspamd_http_options { + RSPAMD_HTTP_BODY_PARTIAL = 0x1//!< RSPAMD_HTTP_BODY_PARTIAL +}; + +struct rspamd_http_server_private; +struct rspamd_http_server; + +typedef gboolean (*rspamd_http_body_handler) (struct rspamd_http_server *srv, + struct rspamd_http_request *req, + const gchar *chunk, + gsize len); + +typedef void (*rspamd_http_error_handler) (struct rspamd_http_server *srv, GError *err); + +typedef void (*rspamd_http_reply_handler) (struct rspamd_http_server *srv, + struct rspamd_http_reply *reply, GError *err); + +/** + * HTTP server structure + */ +struct rspamd_http_server { + gint fd; + struct rspamd_http_server_private *priv; + enum rspamd_http_options opts; + rspamd_http_body_handler body_handler; + rspamd_http_error_handler error_handler; + gpointer ud; +}; + +/** + * Create new http server + * @param handler handler for body + * @param opts options + * @return new server structure + */ +struct rspamd_http_server* rspamd_http_server_new (rspamd_http_body_handler body_handler, + rspamd_http_error_handler error_handler, + enum rspamd_http_options opts); + +/** + * Handle a request using socket fd and user data ud + * @param server server structure + * @param ud opaque user data + * @param fd fd to read/write + */ +void rspamd_http_server_handle_request (struct rspamd_http_server *server, gpointer ud, gint fd, + struct timeval *timeout, struct event_base *base); + +/** + * Send reply using initialised server + * @param server server structure + * @param reply HTTP reply + * @return TRUE if request can be sent + */ +gboolean rspamd_http_server_write_reply (struct rspamd_http_server *server, struct rspamd_http_reply *reply, + rspamd_http_reply_handler *handler); + +/** + * Free server structure + * @param server + */ +void rspamd_http_server_free (struct rspamd_http_server *server); + +/** + * Reset server for a new request + * @param server + */ +void rspamd_http_server_reset (struct rspamd_http_server *server); + +/** + * Create new HTTP reply + * @param code code to pass + * @return new reply object + */ +struct rspamd_http_reply * rspamd_http_new_reply (gint code); + +/** + * Append a header to reply + * @param rep + * @param name + * @param value + */ +void rspamd_http_reply_add_header (struct rspamd_http_reply *rep, const gchar *name, const gchar *value); + +/** + * Free HTTP reply + * @param rep + */ +void rspamd_http_reply_free (struct rspamd_http_reply *rep); + +/** + * Parse HTTP date header and return it as time_t + * @param header HTTP date header + * @param len length of header + * @return time_t or (time_t)-1 in case of error + */ +time_t rspamd_http_parse_date (const gchar *header, gsize len); + +#endif /* HTTP_H_ */ diff --git a/src/lua/lua_http.c b/src/lua/lua_http.c index f83c5f6f4..26f699b78 100644 --- a/src/lua/lua_http.c +++ b/src/lua/lua_http.c @@ -24,6 +24,7 @@ #include "lua_common.h" #include "buffer.h" #include "dns.h" +#include "http.h" #define MAX_HEADERS_SIZE 8192 @@ -229,7 +230,7 @@ lua_http_parse_header_line (struct lua_http_ud *ud, f_str_t *in) /* Check date */ if (g_ascii_strcasecmp (new->name, "date") == 0) { - ud->date = parse_http_date (new->value, -1); + ud->date = rspamd_http_parse_date (new->value, -1); } /* Insert a header to the list */ diff --git a/src/map.c b/src/map.c index ea5b1a35a..9b17bd05e 100644 --- a/src/map.c +++ b/src/map.c @@ -27,6 +27,7 @@ */ #include "config.h" #include "map.h" +#include "http.h" #include "main.h" #include "util.h" #include "mem_pool.h" @@ -349,7 +350,7 @@ read_http_common (struct rspamd_map *map, struct http_map_data *data, struct htt /* Check for date */ date = g_hash_table_lookup (reply->headers, "Date"); if (date != NULL) { - data->last_checked = parse_http_date (date, -1); + data->last_checked = rspamd_http_parse_date (date, -1); } else { data->last_checked = (time_t)-1; diff --git a/src/util.c b/src/util.c index 14af4b2bd..938bb66cc 100644 --- a/src/util.c +++ b/src/util.c @@ -2013,274 +2013,6 @@ parse_ipmask_v4 (const char *line, struct in_addr *ina, int *mask) return TRUE; } -/* - * Obtained from nginx - * Copyright (C) Igor Sysoev - */ -static guint mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - -time_t -parse_http_date (const gchar *header, gsize len) -{ - const gchar *p, *end; - gint month; - guint day, year, hour, min, sec; - guint64 time; - enum { - no = 0, rfc822, /* Tue, 10 Nov 2002 23:50:13 */ - rfc850, /* Tuesday, 10-Dec-02 23:50:13 */ - isoc /* Tue Dec 10 23:50:13 2002 */ - } fmt; - - fmt = 0; - if (len > 0) { - end = header + len; - } - else { - end = header + strlen (header); - } - -#if (NGX_SUPPRESS_WARN) - day = 32; - year = 2038; -#endif - - for (p = header; p < end; p++) { - if (*p == ',') { - break; - } - - if (*p == ' ') { - fmt = isoc; - break; - } - } - - for (p++; p < end; p++) - if (*p != ' ') { - break; - } - - if (end - p < 18) { - return (time_t)-1; - } - - if (fmt != isoc) { - if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { - return (time_t)-1; - } - - day = (*p - '0') * 10 + *(p + 1) - '0'; - p += 2; - - if (*p == ' ') { - if (end - p < 18) { - return (time_t)-1; - } - fmt = rfc822; - - } - else if (*p == '-') { - fmt = rfc850; - - } - else { - return (time_t)-1; - } - - p++; - } - - switch (*p) { - - case 'J': - month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6; - break; - - case 'F': - month = 1; - break; - - case 'M': - month = *(p + 2) == 'r' ? 2 : 4; - break; - - case 'A': - month = *(p + 1) == 'p' ? 3 : 7; - break; - - case 'S': - month = 8; - break; - - case 'O': - month = 9; - break; - - case 'N': - month = 10; - break; - - case 'D': - month = 11; - break; - - default: - return (time_t)-1; - } - - p += 3; - - if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) { - return (time_t)-1; - } - - p++; - - if (fmt == rfc822) { - if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9' - || *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0' - || *(p + 3) > '9') { - return (time_t)-1; - } - - year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100 - + (*(p + 2) - '0') * 10 + *(p + 3) - '0'; - p += 4; - - } - else if (fmt == rfc850) { - if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { - return (time_t)-1; - } - - year = (*p - '0') * 10 + *(p + 1) - '0'; - year += (year < 70) ? 2000 : 1900; - p += 2; - } - - if (fmt == isoc) { - if (*p == ' ') { - p++; - } - - if (*p < '0' || *p > '9') { - return (time_t)-1; - } - - day = *p++ - '0'; - - if (*p != ' ') { - if (*p < '0' || *p > '9') { - return (time_t)-1; - } - - day = day * 10 + *p++ - '0'; - } - - if (end - p < 14) { - return (time_t)-1; - } - } - - if (*p++ != ' ') { - return (time_t)-1; - } - - if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { - return (time_t)-1; - } - - hour = (*p - '0') * 10 + *(p + 1) - '0'; - p += 2; - - if (*p++ != ':') { - return (time_t)-1; - } - - if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { - return (time_t)-1; - } - - min = (*p - '0') * 10 + *(p + 1) - '0'; - p += 2; - - if (*p++ != ':') { - return (time_t)-1; - } - - if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { - return (time_t)-1; - } - - sec = (*p - '0') * 10 + *(p + 1) - '0'; - - if (fmt == isoc) { - p += 2; - - if (*p++ != ' ') { - return (time_t)-1; - } - - if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9' - || *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0' - || *(p + 3) > '9') { - return (time_t)-1; - } - - year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100 - + (*(p + 2) - '0') * 10 + *(p + 3) - '0'; - } - - if (hour > 23 || min > 59 || sec > 59) { - return (time_t)-1; - } - - if (day == 29 && month == 1) { - if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) { - return (time_t)-1; - } - - } - else if (day > mday[month]) { - return (time_t)-1; - } - - /* - * shift new year to March 1 and start months from 1 (not 0), - * it is needed for Gauss' formula - */ - - if (--month <= 0) { - month += 12; - year -= 1; - } - - /* Gauss' formula for Gregorian days since March 1, 1 BC */ - - time = (guint64) ( - /* days in years including leap years since March 1, 1 BC */ - - 365 * year + year / 4 - year / 100 + year / 400 - - /* days before the month */ - - + 367 * month / 12 - 30 - - /* days before the day */ - - + day - 1 - - /* - * 719527 days were between March 1, 1 BC and March 1, 1970, - * 31 and 28 days were in January and February 1970 - */ - - - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec; - - return (time_t) time; -} - static volatile sig_atomic_t saved_signo[NSIG]; static diff --git a/src/util.h b/src/util.h index 910981986..2951942e3 100644 --- a/src/util.h +++ b/src/util.h @@ -432,14 +432,6 @@ gpointer rspamd_str_pool_copy (gconstpointer data, gpointer ud); */ gboolean parse_ipmask_v4 (const char *line, struct in_addr *ina, int *mask); -/** - * Parse HTTP date header and return it as time_t - * @param header HTTP date header - * @param len length of header - * @return time_t or (time_t)-1 in case of error - */ -time_t parse_http_date (const gchar *header, gsize len); - /** * Read passphrase from tty * @param buf buffer to fill with a password -- 2.39.5