rspamd/contrib/librdns/upstream.h

258 righe
9.3 KiB
C

2015-02-21 19:11:12 +01:00
/*
* 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 BY AUTHOR ''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 UPSTREAM_H_
#define UPSTREAM_H_
#include <time.h>
#include <stdio.h>
/**
* @file upstream.h
* The basic macros to define upstream objects
*/
#ifndef upstream_fatal
#define upstream_fatal(msg) do { perror (msg); exit (-1); } while (0)
#endif
#ifndef upstream_malloc
#define upstream_malloc(size) malloc (size)
#endif
#ifndef upstream_free
#define upstream_free(size, ptr) free (ptr)
#endif
struct upstream_entry_s;
struct upstream_common_data {
void **upstreams;
unsigned int allocated_nelts;
unsigned int nelts;
unsigned int alive;
};
typedef struct upstream_entry_s {
unsigned short errors; /**< errors for this upstream */
unsigned short dead;
unsigned short priority;
unsigned short weight;
time_t time; /**< time of marking */
void *parent; /**< parent object */
struct upstream_common_data *common; /**< common data */
void *next; /**< link to the next */
} upstream_entry_t;
/*
* Here we define some reasonable defaults:
* if an upstream has more than `UPSTREAM_MAX_ERRORS` in the period of time
* of `UPSTREAM_ERROR_TIME` then we shut it down for `UPSTREAM_REVIVE_TIME`.
* In this particular case times are 10 seconds for 10 errors and revive in
* 30 seconds.
*/
#ifndef UPSTREAM_REVIVE_TIME
#define UPSTREAM_REVIVE_TIME 30
#endif
#ifndef UPSTREAM_ERROR_TIME
#define UPSTREAM_ERROR_TIME 10
#endif
#ifndef UPSTREAM_MAX_ERRORS
#define UPSTREAM_MAX_ERRORS 10
#endif
#define UPSTREAM_FAIL(u, now) do { \
if ((u)->up.time != 0) { \
if ((now) - (u)->up.time >= UPSTREAM_ERROR_TIME) { \
if ((u)->up.errors >= UPSTREAM_MAX_ERRORS) { \
(u)->up.dead = 1; \
(u)->up.time = now; \
(u)->up.common->alive --; \
} \
else { \
(u)->up.errors = 1; \
(u)->up.time = (now); \
} \
} \
else { \
(u)->up.errors ++; \
} \
} \
else { \
(u)->up.errors ++; \
(u)->up.time = (now); \
} \
} while (0)
#define UPSTREAM_OK(u) do { \
(u)->up.errors = 0; \
(u)->up.time = 0; \
} while (0)
#define UPSTREAM_ADD(head, u, priority) do { \
if (head == NULL) { \
struct upstream_common_data *cd; \
cd = upstream_malloc (sizeof (struct upstream_common_data)); \
if (cd == NULL) { \
upstream_fatal ("malloc failed"); \
} \
cd->upstreams = upstream_malloc (sizeof (void *) * 8); \
if (cd == NULL) { \
upstream_fatal ("malloc failed"); \
} \
cd->allocated_nelts = 8; \
cd->nelts = 1; \
cd->alive = 1; \
cd->upstreams[0] = (u); \
(u)->up.common = cd; \
} \
else { \
struct upstream_common_data *cd = (head)->up.common; \
(u)->up.common = cd; \
if (cd->nelts == cd->allocated_nelts) { \
void **nup; \
nup = upstream_malloc (sizeof (void *) * cd->nelts * 2); \
if (nup == NULL) { \
upstream_fatal ("malloc failed"); \
} \
memcpy (nup, cd->upstreams, cd->nelts * sizeof (void *)); \
upstream_free (cd->nelts * sizeof (void *), cd->upstreams); \
cd->upstreams = nup; \
cd->allocated_nelts *= 2; \
} \
cd->upstreams[cd->nelts++] = (u); \
cd->alive ++; \
} \
(u)->up.next = (head); \
(head) = (u); \
if (priority > 0) { \
(u)->up.priority = (u)->up.weight = (priority); \
} \
else { \
(u)->up.priority = (u)->up.weight = 65535; \
} \
(u)->up.time = 0; \
(u)->up.errors = 0; \
(u)->up.dead = 0; \
(u)->up.parent = (u); \
} while (0)
#define UPSTREAM_DEL(head, u) do { \
if (head != NULL) { \
struct upstream_common_data *cd = (head)->up.common; \
if ((u)->up.next != NULL) { \
(head) = (u)->up.next; \
cd->nelts --; \
cd->alive --; \
} \
else { \
upstream_free (cd->allocated_nelts * sizeof (void *), \
cd->upstreams); \
upstream_free (sizeof (struct upstream_common_data), cd); \
(head) = NULL; \
} \
} \
} while (0)
#define UPSTREAM_FOREACH(head, u) for ((u) = (head); (u) != NULL; (u) = (u)->up.next)
#define UPSTREAM_FOREACH_SAFE(head, u, tmp) \
for ((u) = (head); \
(u) != NULL && ((tmp = (u)->up.next) || true); \
(u) = (tmp))
#define UPSTREAM_REVIVE_ALL(head) do { \
__typeof(head) elt = (head); \
while (elt != NULL) { \
elt->up.dead = 0; \
elt->up.errors = 0; \
elt->up.time = 0; \
elt = elt->up.next; \
} \
(head)->up.common->alive = (head)->up.common->nelts; \
} while (0)
#define UPSTREAM_RESCAN(head, now) do { \
__typeof(head) elt = (head); \
if ((head)->up.common->alive == 0) { \
UPSTREAM_REVIVE_ALL((head)); \
} \
else { \
while (elt != NULL) { \
if (elt->up.dead) { \
if ((now) - elt->up.time >= UPSTREAM_REVIVE_TIME) { \
elt->up.dead = 0; \
elt->up.errors = 0; \
elt->up.weight = elt->up.priority; \
(head)->up.common->alive ++; \
} \
} \
else { \
if ((now) - elt->up.time >= UPSTREAM_ERROR_TIME && \
elt->up.errors >= UPSTREAM_MAX_ERRORS) { \
elt->up.dead = 1; \
elt->up.time = now; \
(head)->up.common->alive --; \
} \
} \
elt = elt->up.next; \
} \
} \
} while (0)
#define UPSTREAM_SELECT_ROUND_ROBIN(head, selected) do { \
__typeof(head) elt = (head); \
(selected) = NULL; \
int alive = 0; \
unsigned max_weight = 0; \
if ((head)->up.common->alive == 0){ \
UPSTREAM_REVIVE_ALL(head); \
} \
while (elt != NULL) { \
if (!elt->up.dead) { \
if (elt->up.weight > max_weight) { \
max_weight = elt->up.weight; \
(selected) = elt; \
} \
alive ++; \
} \
elt = elt->up.next; \
} \
if (max_weight == 0) { \
elt = (head); \
while (elt != NULL) { \
elt->up.weight = elt->up.priority; \
if (!elt->up.dead) { \
if (elt->up.priority > max_weight) { \
max_weight = elt->up.priority; \
(selected) = elt; \
} \
} \
elt = elt->up.next; \
} \
} \
(selected)->up.weight --; \
} while (0)
#endif /* UPSTREAM_H_ */