diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2013-06-26 17:59:50 +0100 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2013-06-26 17:59:50 +0100 |
commit | a0f4aea00904ebb17c23b172b8cd8981effde6e2 (patch) | |
tree | 2c27f76c0bdedcc0ee802744776dfaa0cfb858fd | |
parent | 309c486ae9bd82bb34190d50a7d86f2a876c868a (diff) | |
download | rspamd-a0f4aea00904ebb17c23b172b8cd8981effde6e2.tar.gz rspamd-a0f4aea00904ebb17c23b172b8cd8981effde6e2.zip |
Parse HTTP date in map requests.
-rw-r--r-- | src/map.c | 22 | ||||
-rw-r--r-- | src/util.c | 270 | ||||
-rw-r--r-- | src/util.h | 8 |
3 files changed, 294 insertions, 6 deletions
@@ -305,7 +305,7 @@ read_http_common (struct rspamd_map *map, struct http_map_data *data, struct htt { gchar *remain, *pos; ssize_t r; - gchar *te; + gchar *te, *date; if ((r = read (fd, data->read_buf + data->rlen, sizeof (data->read_buf) - data->rlen)) > 0) { r += data->rlen; @@ -346,6 +346,14 @@ read_http_common (struct rspamd_map *map, struct http_map_data *data, struct htt data->chunked = -1; } } + /* Check for date */ + date = g_hash_table_lookup (reply->headers, "Date"); + if (date != NULL) { + data->last_checked = parse_http_date (date, -1); + } + else { + data->last_checked = (time_t)-1; + } if (data->chunked > 0) { return read_http_chunked (data->read_buf, r, map, data, cbdata); @@ -402,7 +410,9 @@ read_http_sync (struct rspamd_map *map, struct http_map_data *data) map->fin_callback (map->pool, &cbdata); *map->user_data = cbdata.cur_data; - data->last_checked = time (NULL); + if (data->last_checked == (time_t)-1) { + data->last_checked = time (NULL); + } g_hash_table_destroy (repl->headers); g_free (repl); @@ -846,14 +856,18 @@ http_async_callback (gint fd, short what, void *ud) if (!read_http_common (cbd->map, cbd->data, cbd->reply, &cbd->cbdata, cbd->fd)) { /* Handle Not-Modified in a special way */ if (cbd->reply->code == 304) { - cbd->data->last_checked = time (NULL); + if (cbd->data->last_checked == (time_t)-1) { + cbd->data->last_checked = time (NULL); + } msg_info ("data is not modified for server %s", cbd->data->host); } else if (cbd->cbdata.cur_data != NULL) { /* Destroy old data and start reading request data */ cbd->map->fin_callback (cbd->map->pool, &cbd->cbdata); *cbd->map->user_data = cbd->cbdata.cur_data; - cbd->data->last_checked = time (NULL); + if (cbd->data->last_checked == (time_t)-1) { + cbd->data->last_checked = time (NULL); + } } if (cbd->state == 1 && cbd->reply->code == 200) { /* Write to log that data is modified */ diff --git a/src/util.c b/src/util.c index 7757c7ad0..441ce007a 100644 --- a/src/util.c +++ b/src/util.c @@ -38,8 +38,6 @@ /* Default connect timeout for sync sockets */ #define CONNECT_TIMEOUT 3 -static gchar* rspamd_sprintf_num (gchar *buf, gchar *last, guint64 ui64, gchar zero, guint hexadecimal, guint width); - gint make_socket_nonblocking (gint fd) { @@ -1860,5 +1858,273 @@ parse_ipmask_v4 (const char *line, struct in_addr *ina, int *mask) } /* + * 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; +} + +/* * vi:ts=4 */ diff --git a/src/util.h b/src/util.h index 228fb7445..b1e07c538 100644 --- a/src/util.h +++ b/src/util.h @@ -400,4 +400,12 @@ 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); + #endif |