Quellcode durchsuchen

Implement http server using http_parser.

tags/0.6.7
Vsevolod Stakhov vor 10 Jahren
Ursprung
Commit
4e4ae88f0e
8 geänderte Dateien mit 764 neuen und 279 gelöschten Zeilen
  1. 3
    1
      CMakeLists.txt
  2. 2
    0
      lib/CMakeLists.txt
  3. 591
    0
      src/http.c
  4. 164
    0
      src/http.h
  5. 2
    1
      src/lua/lua_http.c
  6. 2
    1
      src/map.c
  7. 0
    268
      src/util.c
  8. 0
    8
      src/util.h

+ 3
- 1
CMakeLists.txt Datei anzeigen

@@ -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)

+ 2
- 0
lib/CMakeLists.txt Datei anzeigen

@@ -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)

+ 591
- 0
src/http.c Datei anzeigen

@@ -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;
}

+ 164
- 0
src/http.h Datei anzeigen

@@ -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_ */

+ 2
- 1
src/lua/lua_http.c Datei anzeigen

@@ -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 */

+ 2
- 1
src/map.c Datei anzeigen

@@ -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;

+ 0
- 268
src/util.c Datei anzeigen

@@ -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

+ 0
- 8
src/util.h Datei anzeigen

@@ -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

Laden…
Abbrechen
Speichern