Identify maps by id.
Initialize secure random numbers using openssl.
Add description to maps.
if (g_ascii_strncasecmp (str, "no", len) == 0) {
return 0;
}
+ else if (g_ascii_strncasecmp (str, "on", len) == 0) {
+ return 1;
+ }
break;
case 3:
if (g_ascii_strncasecmp (str, "yes", len) == 0) {
return 1;
}
+ else if (g_ascii_strncasecmp (str, "off", len) == 0) {
+ return 0;
+ }
break;
case 4:
if (g_ascii_strncasecmp (str, "true", len) == 0) {
gboolean
handle_user_settings (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset)
{
- if (!read_settings (data, cfg, cfg->user_settings)) {
+ if (!read_settings (data, "Users' settings", cfg, cfg->user_settings)) {
msg_err ("cannot read settings %s", data);
return FALSE;
}
gboolean
handle_domain_settings (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset)
{
- if (!read_settings (data, cfg, cfg->domain_settings)) {
+ if (!read_settings (data, "Domains' settings", cfg, cfg->domain_settings)) {
msg_err ("cannot read settings %s", data);
return FALSE;
}
jb->buf = NULL;
jb->cfg = cfg;
*pjb = jb;
- if (!add_map (cfg, cfg->dynamic_conf, json_config_read_cb, json_config_fin_cb, (void **)pjb)) {
+ if (!add_map (cfg, cfg->dynamic_conf, "Dynamic configuration map", json_config_read_cb, json_config_fin_cb, (void **)pjb)) {
msg_err ("cannot add map for configuration %s", cfg->dynamic_conf);
}
}
radix_tree_free (rspamd->logger->debug_ip);
}
rspamd->logger->debug_ip = radix_tree_create ();
- if (!add_map (rspamd->cfg, rspamd->cfg->debug_ip_map, read_radix_list, fin_radix_list, (void **)&rspamd->logger->debug_ip)) {
+ if (!add_map (rspamd->cfg, rspamd->cfg->debug_ip_map, "IP addresses for which debug logs are enabled",
+ read_radix_list, fin_radix_list, (void **)&rspamd->logger->debug_ip)) {
/* Try to parse it as list */
strvec = g_strsplit_set (rspamd->cfg->debug_ip_map, ",; ", 0);
num = g_strv_length (strvec);
lua_config_add_radix_map (lua_State *L)
{
struct config_file *cfg = lua_check_config (L);
- const gchar *map_line;
+ const gchar *map_line, *description;
radix_tree_t **r, ***ud;
if (cfg) {
map_line = luaL_checkstring (L, 2);
+ description = lua_tostring (L, 3);
r = memory_pool_alloc (cfg->cfg_pool, sizeof (radix_tree_t *));
*r = radix_tree_create ();
- if (!add_map (cfg, map_line, read_radix_list, fin_radix_list, (void **)r)) {
+ if (!add_map (cfg, map_line, description, read_radix_list, fin_radix_list, (void **)r)) {
msg_warn ("invalid radix map %s", map_line);
radix_tree_free (*r);
lua_pushnil (L);
lua_config_add_hash_map (lua_State *L)
{
struct config_file *cfg = lua_check_config (L);
- const gchar *map_line;
+ const gchar *map_line, *description;
GHashTable **r, ***ud;
if (cfg) {
map_line = luaL_checkstring (L, 2);
+ description = lua_tostring (L, 3);
r = memory_pool_alloc (cfg->cfg_pool, sizeof (GHashTable *));
*r = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal);
- if (!add_map (cfg, map_line, read_host_list, fin_host_list, (void **)r)) {
+ if (!add_map (cfg, map_line, description, read_host_list, fin_host_list, (void **)r)) {
msg_warn ("invalid hash map %s", map_line);
g_hash_table_destroy (*r);
lua_pushnil (L);
lua_config_add_kv_map (lua_State *L)
{
struct config_file *cfg = lua_check_config (L);
- const gchar *map_line;
+ const gchar *map_line, *description;
GHashTable **r, ***ud;
if (cfg) {
map_line = luaL_checkstring (L, 2);
+ description = lua_tostring (L, 3);
r = memory_pool_alloc (cfg->cfg_pool, sizeof (GHashTable *));
*r = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal);
- if (!add_map (cfg, map_line, read_kv_list, fin_kv_list, (void **)r)) {
+ if (!add_map (cfg, map_line, description, read_kv_list, fin_kv_list, (void **)r)) {
msg_warn ("invalid hash map %s", map_line);
g_hash_table_destroy (*r);
lua_pushnil (L);
#include "cfg_xml.h"
#include "symbols_cache.h"
#include "lua/lua_common.h"
+#ifdef HAVE_OPENSSL
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#endif
/* 2 seconds to fork new process in place of dead one */
#define SOFT_FORK_TIME 2
GList *l;
worker_t **pworker;
GQuark type;
+#ifdef HAVE_OPENSSL
+ gchar rand_bytes[sizeof (guint32)];
+ guint32 rand_seed;
+#endif
#ifdef HAVE_SA_SIGINFO
signals_info = g_queue_new ();
setlocale (LC_TIME, "C");
#endif
+ /* Init random generator */
+#ifdef HAVE_OPENSSL
+ if (RAND_bytes (rand_bytes, sizeof (rand_bytes)) != 1) {
+ msg_err ("cannot seed random generator using openssl: %s, using time", ERR_error_string (ERR_get_error (), NULL));
+ g_random_set_seed (time (NULL));
+ }
+ else {
+ memcpy (&rand_seed, rand_bytes, sizeof (guint32));
+ g_random_set_seed (rand_seed);
+ }
+#endif
+
/* First set logger to console logger */
rspamd_set_logger (RSPAMD_LOG_CONSOLE, type, rspamd_main);
(void)open_log (rspamd_main->logger);
while (cur) {
map = cur->data;
map->ev_base = ev_base;
- if (map->protocol == PROTO_FILE) {
+ if (map->protocol == MAP_PROTO_FILE) {
evtimer_set (&map->ev, file_callback, map);
event_base_set (map->ev_base, &map->ev);
/* Read initial data */
map->tv.tv_usec = 0;
evtimer_add (&map->ev, &map->tv);
}
- else if (map->protocol == PROTO_HTTP) {
+ else if (map->protocol == MAP_PROTO_HTTP) {
evtimer_set (&map->ev, http_callback, map);
event_base_set (map->ev_base, &map->ev);
/* Read initial data */
{
if (g_ascii_strncasecmp (map_line, "http://", sizeof ("http://") - 1) == 0) {
if (res && pos) {
- *res = PROTO_HTTP;
+ *res = MAP_PROTO_HTTP;
*pos = map_line + sizeof ("http://") - 1;
}
}
else if (g_ascii_strncasecmp (map_line, "file://", sizeof ("file://") - 1) == 0) {
if (res && pos) {
- *res = PROTO_FILE;
+ *res = MAP_PROTO_FILE;
*pos = map_line + sizeof ("file://") - 1;
}
}
else if (*map_line == '/') {
/* Trivial file case */
- *res = PROTO_FILE;
+ *res = MAP_PROTO_FILE;
*pos = map_line;
}
else {
}
gboolean
-add_map (struct config_file *cfg, const gchar *map_line, map_cb_t read_callback, map_fin_cb_t fin_callback, void **user_data)
+add_map (struct config_file *cfg, const gchar *map_line, const gchar *description,
+ map_cb_t read_callback, map_fin_cb_t fin_callback, void **user_data)
{
struct rspamd_map *new_map;
enum fetch_proto proto;
- const gchar *def, *p, *hostend;
+ const gchar *def, *p, *hostend;
struct file_map_data *fdata;
struct http_map_data *hdata;
gchar portbuf[6];
new_map->user_data = user_data;
new_map->protocol = proto;
new_map->cfg = cfg;
+ new_map->uri = memory_pool_strdup (cfg->cfg_pool, proto == MAP_PROTO_FILE ? def : map_line);
+ new_map->id = g_random_int ();
+ if (description != NULL) {
+ new_map->description = memory_pool_strdup (cfg->cfg_pool, description);
+ }
/* Now check for each proto separately */
- if (proto == PROTO_FILE) {
+ if (proto == MAP_PROTO_FILE) {
fdata = memory_pool_alloc0 (cfg->map_pool, sizeof (struct file_map_data));
if (access (def, R_OK) == -1) {
if (errno != ENOENT) {
fdata->filename = memory_pool_strdup (cfg->map_pool, def);
new_map->map_data = fdata;
}
- else if (proto == PROTO_HTTP) {
+ else if (proto == MAP_PROTO_HTTP) {
hdata = memory_pool_alloc0 (cfg->map_pool, sizeof (struct http_map_data));
/* Try to search port */
if ((p = strchr (def, ':')) != NULL) {
*/
enum fetch_proto {
- PROTO_FILE,
- PROTO_HTTP,
+ MAP_PROTO_FILE,
+ MAP_PROTO_HTTP,
};
/**
struct timeval tv;
struct event_base *ev_base;
void *map_data;
+ gchar *uri;
+ gchar *description;
+ guint32 id;
};
/**
/**
* Add map from line
*/
-gboolean add_map (struct config_file *cfg, const gchar *map_line, map_cb_t read_callback, map_fin_cb_t fin_callback, void **user_data);
+gboolean add_map (struct config_file *cfg, const gchar *map_line, const gchar *description,
+ map_cb_t read_callback, map_fin_cb_t fin_callback, void **user_data);
/**
* Start watching of maps by adding events to libevent event loop
dkim_module_ctx->time_jitter = DEFAULT_TIME_JITTER;
}
if ((value = get_module_opt (cfg, "dkim", "whitelist")) != NULL) {
- if (! add_map (cfg, value, read_radix_list, fin_radix_list, (void **)&dkim_module_ctx->whitelist_ip)) {
+ if (! add_map (cfg, value, "DKIM whitelist", read_radix_list, fin_radix_list, (void **)&dkim_module_ctx->whitelist_ip)) {
msg_warn ("cannot load whitelist from %s", value);
}
}
if ((value = get_module_opt (cfg, "dkim", "domains")) != NULL) {
- if (! add_map (cfg, value, read_kv_list, fin_kv_list, (void **)&dkim_module_ctx->dkim_domains)) {
+ if (! add_map (cfg, value, "DKIM domains", read_kv_list, fin_kv_list, (void **)&dkim_module_ctx->dkim_domains)) {
msg_warn ("cannot load dkim domains list from %s", value);
}
else {
if ((value = get_module_opt (cfg, "fuzzy_check", "whitelist")) != NULL) {
fuzzy_module_ctx->whitelist = radix_tree_create ();
- if (!add_map (cfg, value, read_radix_list, fin_radix_list, (void **)&fuzzy_module_ctx->whitelist)) {
+ if (!add_map (cfg, value, "Fuzzy whitelist", read_radix_list, fin_radix_list, (void **)&fuzzy_module_ctx->whitelist)) {
msg_err ("cannot add whitelist '%s'", value);
}
}
jb->buf = NULL;
jb->cfg = cfg;
*pjb = jb;
- if (!add_map (cfg, value, json_regexp_read_cb, json_regexp_fin_cb, (void **)pjb)) {
+ if (!add_map (cfg, value, "Dynamic regexp rules", json_regexp_read_cb, json_regexp_fin_cb, (void **)pjb)) {
msg_err ("cannot add map %s", value);
}
}
cache_expire = DEFAULT_CACHE_MAXAGE;
}
if ((value = get_module_opt (cfg, "spf", "whitelist")) != NULL) {
- if (! add_map (cfg, value, read_radix_list, fin_radix_list, (void **)&spf_module_ctx->whitelist_ip)) {
+ if (! add_map (cfg, value, "SPF whitelist", read_radix_list, fin_radix_list, (void **)&spf_module_ctx->whitelist_ip)) {
msg_warn ("cannot load whitelist from %s", value);
}
}
surbl_module_ctx->read_timeout = DEFAULT_REDIRECTOR_READ_TIMEOUT;
}
if ((value = get_module_opt (cfg, "surbl", "redirector_hosts_map")) != NULL) {
- add_map (cfg, value, read_redirectors_list, fin_redirectors_list, (void **)&surbl_module_ctx->redirector_hosts);
+ add_map (cfg, value, "SURBL redirectors list", read_redirectors_list, fin_redirectors_list, (void **)&surbl_module_ctx->redirector_hosts);
}
else {
surbl_module_ctx->read_timeout = DEFAULT_REDIRECTOR_READ_TIMEOUT;
surbl_module_ctx->max_urls = DEFAULT_SURBL_MAX_URLS;
}
if ((value = get_module_opt (cfg, "surbl", "exceptions")) != NULL) {
- if (add_map (cfg, value, read_exceptions_list, fin_exceptions_list, (void **)&surbl_module_ctx->exceptions)) {
+ if (add_map (cfg, value, "SURBL exceptions list", read_exceptions_list, fin_exceptions_list, (void **)&surbl_module_ctx->exceptions)) {
surbl_module_ctx->tld2_file = memory_pool_strdup (surbl_module_ctx->surbl_pool, value + sizeof ("file://") - 1);
}
}
if ((value = get_module_opt (cfg, "surbl", "whitelist")) != NULL) {
- if (add_map (cfg, value, read_host_list, fin_host_list, (void **)&surbl_module_ctx->whitelist)) {
+ if (add_map (cfg, value, "SURBL whitelist", read_host_list, fin_host_list, (void **)&surbl_module_ctx->whitelist)) {
surbl_module_ctx->whitelist_file = memory_pool_strdup (surbl_module_ctx->surbl_pool, value + sizeof ("file://") - 1);
}
}
}
gboolean
-read_settings (const gchar *path, struct config_file *cfg, GHashTable * table)
+read_settings (const gchar *path, const gchar *description, struct config_file *cfg, GHashTable * table)
{
struct json_buf *jb = g_malloc (sizeof (struct json_buf)), **pjb;
jb->buf = NULL;
*pjb = jb;
- if (!add_map (cfg, path, json_read_cb, json_fin_cb, (void **)pjb)) {
+ if (!add_map (cfg, path, description, json_read_cb, json_fin_cb, (void **)pjb)) {
msg_err ("cannot add map %s", path);
return FALSE;
}
/*
* Read settings from specified path
*/
-gboolean read_settings (const gchar *path, struct config_file *cfg, GHashTable *table);
+gboolean read_settings (const gchar *path, const gchar *description, struct config_file *cfg, GHashTable *table);
/*
* Init configuration structures for settings
{
struct rspamd_regexp *re = NULL;
- if (add_map (view->cfg, line, read_host_list, fin_host_list, (void **)&view->from_hash)) {
+ if (add_map (view->cfg, line, "SMTP From view", read_host_list, fin_host_list, (void **)&view->from_hash)) {
return TRUE;
}
else if ((re = parse_regexp (view->pool, line, TRUE)) != NULL) {
{
struct rspamd_regexp *re = NULL;
- if (add_map (view->cfg, line, read_host_list, fin_host_list, (void **)&view->rcpt_hash)) {
+ if (add_map (view->cfg, line, "Recipients view", read_host_list, fin_host_list, (void **)&view->rcpt_hash)) {
return TRUE;
}
else if ((re = parse_regexp (view->pool, line, TRUE)) != NULL) {
struct rspamd_regexp *re = NULL;
GList *symbols;
- if (add_map (view->cfg, line, read_host_list, fin_host_list, (void **)&view->symbols_hash)) {
+ if (add_map (view->cfg, line, "Symbols view", read_host_list, fin_host_list, (void **)&view->symbols_hash)) {
return TRUE;
}
else if ((re = parse_regexp (view->pool, line, TRUE)) != NULL) {
gboolean
add_view_ip (struct rspamd_view * view, gchar *line)
{
- if (add_map (view->cfg, line, read_radix_list, fin_radix_list, (void **)&view->ip_tree)) {
+ if (add_map (view->cfg, line, "IP view", read_radix_list, fin_radix_list, (void **)&view->ip_tree)) {
return TRUE;
}
gboolean
add_view_client_ip (struct rspamd_view * view, gchar *line)
{
- if (add_map (view->cfg, line, read_radix_list, fin_radix_list, (void **)&view->client_ip_tree)) {
+ if (add_map (view->cfg, line, "Client IP view", read_radix_list, fin_radix_list, (void **)&view->client_ip_tree)) {
return TRUE;
}
/* HTTP paths */
#define PATH_AUTH "/login"
#define PATH_SYMBOLS "/symbols"
+#define PATH_MAPS "/maps"
+#define PATH_GET_MAP "/getmap"
gpointer init_webui_worker (void);
void start_webui_worker (struct rspamd_worker *worker);
evb = evbuffer_new ();
if (!evb) {
msg_err ("cannot allocate evbuffer for reply");
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL);
return;
}
evb = evbuffer_new ();
if (!evb) {
msg_err ("cannot allocate evbuffer for reply");
+ evhttp_send_reply (req, HTTP_INTERNAL, "500", NULL);
return;
}
evbuffer_free (evb);
}
+/*
+ * Maps command handler:
+ * request: /maps
+ * headers: Password
+ * reply: json [
+ * {
+ * "map": "name",
+ * "description": "description",
+ * "editable": true
+ * },
+ * {...}
+ * ]
+ */
+static void
+http_handle_maps (struct evhttp_request *req, gpointer arg)
+{
+ struct rspamd_webui_worker_ctx *ctx = arg;
+ struct evbuffer *evb;
+ GList *cur;
+ struct rspamd_map *map;
+ gboolean editable;
+
+ evb = evbuffer_new ();
+ if (!evb) {
+ msg_err ("cannot allocate evbuffer for reply");
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL);
+ return;
+ }
+
+ /* Trailer */
+ evbuffer_add (evb, "[", 1);
+
+ /* Iterate over all maps */
+ cur = ctx->cfg->maps;
+ while (cur) {
+ map = cur->data;
+ if (map->protocol == MAP_PROTO_FILE && map->description != NULL) {
+ if (access (map->uri, R_OK) == 0) {
+ editable = access (map->uri, W_OK) == 0;
+ evbuffer_add_printf (evb, "{\"map\":%u,\"description\":\"%s\",\"editable\":%s%s",
+ map->id, map->description, editable ? "true" : "false",
+ g_list_next (cur) ? "}," : "}");
+ }
+ }
+ cur = g_list_next (cur);
+ }
+
+ evbuffer_add (evb, "]" CRLF, 3);
+ evhttp_add_header (req->output_headers, "Connection", "close");
+ http_calculate_content_length (evb, req);
+
+ evhttp_send_reply (req, HTTP_OK, "OK", evb);
+ evbuffer_free (evb);
+}
+
+/*
+ * Get map command handler:
+ * request: /getmap
+ * headers: Password, Map
+ * reply: plain-text
+ */
+static void
+http_handle_get_map (struct evhttp_request *req, gpointer arg)
+{
+ struct rspamd_webui_worker_ctx *ctx = arg;
+ struct evbuffer *evb;
+ GList *cur;
+ struct rspamd_map *map;
+ const gchar *idstr;
+ gchar *errstr;
+ struct stat st;
+ gint fd;
+ guint32 id;
+ gboolean found = FALSE;
+
+ evb = evbuffer_new ();
+ if (!evb) {
+ msg_err ("cannot allocate evbuffer for reply");
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL);
+ return;
+ }
+
+ idstr = evhttp_find_header (req->input_headers, "Map");
+
+ if (idstr == NULL) {
+ msg_info ("absent map id");
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 map open error", NULL);
+ return;
+ }
+
+ id = strtoul (idstr, &errstr, 10);
+ if (*errstr != '\0') {
+ msg_info ("invalid map id");
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 map open error", NULL);
+ return;
+ }
+
+ /* Now let's be sure that we have map defined in configuration */
+ cur = ctx->cfg->maps;
+ while (cur) {
+ map = cur->data;
+ if (map->id == id && map->protocol == MAP_PROTO_FILE) {
+ found = TRUE;
+ break;
+ }
+ cur = g_list_next (cur);
+ }
+
+ if (!found) {
+ msg_info ("map not found");
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_NOTFOUND, "404 map not found", NULL);
+ return;
+ }
+
+ if (stat (map->uri, &st) == -1 || (fd = open (map->uri, O_RDONLY)) == -1) {
+ msg_err ("cannot open map %s: %s", map->uri, strerror (errno));
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 map open error", NULL);
+ return;
+ }
+ /* Set buffer size */
+ if (evbuffer_expand (evb, st.st_size) != 0) {
+ msg_err ("cannot allocate buffer for map %s: %s", map->uri, strerror (errno));
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL);
+ close (fd);
+ return;
+ }
+
+ /* Read the whole buffer */
+ if (evbuffer_read (evb, fd, st.st_size) == -1) {
+ msg_err ("cannot read map %s: %s", map->uri, strerror (errno));
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 map read error", NULL);
+ close (fd);
+ return;
+ }
+
+ evhttp_add_header (req->output_headers, "Connection", "close");
+ http_calculate_content_length (evb, req);
+
+ close (fd);
+
+ evhttp_send_reply (req, HTTP_OK, "OK", evb);
+ evbuffer_free (evb);
+}
+
+
gpointer
init_webui_worker (void)
{
/* Add callbacks for different methods */
evhttp_set_cb (ctx->http, PATH_AUTH, http_handle_auth, ctx);
evhttp_set_cb (ctx->http, PATH_SYMBOLS, http_handle_symbols, ctx);
+ evhttp_set_cb (ctx->http, PATH_MAPS, http_handle_maps, ctx);
+ evhttp_set_cb (ctx->http, PATH_GET_MAP, http_handle_get_map, ctx);
ctx->resolver = dns_resolver_init (ctx->ev_base, worker->srv->cfg);