/*** * @function rspamd_cryptobox_pubkey.load(file[, type[, alg]]) * Loads public key from base32 encoded file * @param {string} file filename to load * @param {string} type optional 'sign' or 'kex' for signing and encryption * @param {string} alg optional 'default' or 'nist' for curve25519/nistp256 keys * @return {cryptobox_pubkey} new public key */ static gint lua_cryptobox_pubkey_load (lua_State *L) { struct rspamd_cryptobox_pubkey *pkey = NULL, **ppkey; const gchar *filename, *arg; gint type = RSPAMD_KEYPAIR_SIGN; gint alg = RSPAMD_CRYPTOBOX_MODE_25519; guchar *map; gsize len; filename = luaL_checkstring (L, 1); if (filename != NULL) { map = rspamd_file_xmap (filename, PROT_READ, &len); if (map == NULL) { msg_err ("cannot open pubkey from file: %s, %s", filename, strerror (errno)); lua_pushnil (L); } else { if (lua_type (L, 2) == LUA_TSTRING) { /* keypair type */ arg = lua_tostring (L, 2); if (strcmp (arg, "sign") == 0) { type = RSPAMD_KEYPAIR_SIGN; } else if (strcmp (arg, "kex") == 0) { type = RSPAMD_KEYPAIR_KEX; } } if (lua_type (L, 3) == LUA_TSTRING) { /* algorithm */ arg = lua_tostring (L, 3); if (strcmp (arg, "default") == 0 || strcmp (arg, "curve25519") == 0) { type = RSPAMD_CRYPTOBOX_MODE_25519; } else if (strcmp (arg, "nist") == 0) { type = RSPAMD_CRYPTOBOX_MODE_NIST; } } pkey = rspamd_pubkey_from_base32 (map, len, type, alg); if (pkey == NULL) { msg_err ("cannot open pubkey from file: %s", filename); munmap (map, len); lua_pushnil (L); } else { munmap (map, len); ppkey = lua_newuserdata (L, sizeof (void *)); rspamd_lua_setclass (L, "rspamd{cryptobox_pubkey}", -1); *ppkey = pkey; } } } else { return luaL_error (L, "bad input arguments"); } return 1; }
/*** * @function rspamd_tcp.request({params}) * This function creates and sends TCP request to the specified host and port, * resolves hostname (if needed) and invokes continuation callback upon data received * from the remote peer. This function accepts table of arguments with the following * attributes * * - `task`: rspamd task objects (implies `pool`, `session`, `ev_base` and `resolver` arguments) * - `ev_base`: event base (if no task specified) * - `resolver`: DNS resolver (no task) * - `session`: events session (no task) * - `pool`: memory pool (no task) * - `host`: IP or name of the peer (required) * - `port`: remote port to use (required) * - `data`: a table of strings or `rspamd_text` objects that contains data pieces * - `callback`: continuation function (required) * - `timeout`: floating point value that specifies timeout for IO operations in seconds * - `partial`: boolean flag that specifies that callback should be called on any data portion received * - `stop_pattern`: stop reading on finding a certain pattern (e.g. \r\n.\r\n for smtp) * @return {boolean} true if request has been sent */ static gint lua_tcp_request (lua_State *L) { const gchar *host; gchar *stop_pattern = NULL; guint port; gint cbref, tp; struct event_base *ev_base; struct lua_tcp_cbdata *cbd; struct rspamd_dns_resolver *resolver; struct rspamd_async_session *session; struct rspamd_task *task = NULL; rspamd_mempool_t *pool; struct iovec *iov = NULL; guint niov = 0, total_out; gdouble timeout = default_tcp_timeout; gboolean partial = FALSE; if (lua_type (L, 1) == LUA_TTABLE) { lua_pushstring (L, "host"); lua_gettable (L, -2); host = luaL_checkstring (L, -1); lua_pop (L, 1); lua_pushstring (L, "port"); lua_gettable (L, -2); port = luaL_checknumber (L, -1); lua_pop (L, 1); lua_pushstring (L, "callback"); lua_gettable (L, -2); if (host == NULL || lua_type (L, -1) != LUA_TFUNCTION) { lua_pop (L, 1); msg_err ("tcp request has bad params"); lua_pushboolean (L, FALSE); return 1; } cbref = luaL_ref (L, LUA_REGISTRYINDEX); lua_pushstring (L, "task"); lua_gettable (L, -2); if (lua_type (L, -1) == LUA_TUSERDATA) { task = lua_check_task (L, -1); ev_base = task->ev_base; resolver = task->resolver; session = task->s; pool = task->task_pool; } lua_pop (L, 1); if (task == NULL) { lua_pushstring (L, "ev_base"); lua_gettable (L, -2); if (luaL_checkudata (L, -1, "rspamd{ev_base}")) { ev_base = *(struct event_base **)lua_touserdata (L, -1); } else { ev_base = NULL; } lua_pop (L, 1); lua_pushstring (L, "pool"); lua_gettable (L, -2); if (luaL_checkudata (L, -1, "rspamd{mempool}")) { pool = *(rspamd_mempool_t **)lua_touserdata (L, -1); } else { pool = NULL; } lua_pop (L, 1); lua_pushstring (L, "resolver"); lua_gettable (L, -2); if (luaL_checkudata (L, -1, "rspamd{resolver}")) { resolver = *(struct rspamd_dns_resolver **)lua_touserdata (L, -1); } else { resolver = lua_tcp_global_resolver (ev_base); } lua_pop (L, 1); lua_pushstring (L, "session"); lua_gettable (L, -2); if (luaL_checkudata (L, -1, "rspamd{session}")) { session = *(struct rspamd_async_session **)lua_touserdata (L, -1); } else { session = NULL; } lua_pop (L, 1); } lua_pushstring (L, "timeout"); lua_gettable (L, -2); if (lua_type (L, -1) == LUA_TNUMBER) { timeout = lua_tonumber (L, -1) * 1000.; } lua_pop (L, 1); lua_pushstring (L, "stop_pattern"); lua_gettable (L, -2); if (lua_type (L, -1) == LUA_TSTRING) { stop_pattern = rspamd_mempool_strdup (pool, lua_tostring (L, -1)); } lua_pop (L, 1); lua_pushstring (L, "partial"); lua_gettable (L, -2); if (lua_type (L, -1) == LUA_TBOOLEAN) { partial = lua_toboolean (L, -1); } lua_pop (L, 1); if (pool == NULL) { lua_pop (L, 1); msg_err ("tcp request has no memory pool associated"); lua_pushboolean (L, FALSE); return 1; } lua_pushstring (L, "data"); lua_gettable (L, -2); total_out = 0; tp = lua_type (L, -1); if (tp == LUA_TSTRING || tp == LUA_TUSERDATA) { iov = rspamd_mempool_alloc (pool, sizeof (*iov)); niov = 1; if (!lua_tcp_arg_toiovec (L, -1, pool, iov)) { lua_pop (L, 1); msg_err ("tcp request has bad data argument"); lua_pushboolean (L, FALSE); return 1; } total_out = iov[0].iov_len; } else if (tp == LUA_TTABLE) { /* Count parts */ lua_pushnil (L); while (lua_next (L, -2) != 0) { niov ++; lua_pop (L, 1); } iov = rspamd_mempool_alloc (pool, sizeof (*iov) * niov); lua_pushnil (L); niov = 0; while (lua_next (L, -2) != 0) { if (!lua_tcp_arg_toiovec (L, -1, pool, &iov[niov])) { lua_pop (L, 2); msg_err ("tcp request has bad data argument at pos %d", niov); lua_pushboolean (L, FALSE); return 1; } total_out += iov[niov].iov_len; niov ++; lua_pop (L, 1); } } lua_pop (L, 1); } else { msg_err ("tcp request has bad params"); lua_pushboolean (L, FALSE); return 1; } cbd = g_slice_alloc0 (sizeof (*cbd)); cbd->L = L; cbd->cbref = cbref; cbd->ev_base = ev_base; msec_to_tv (timeout, &cbd->tv); cbd->fd = -1; cbd->pool = pool; cbd->partial = partial; cbd->iov = iov; cbd->iovlen = niov; cbd->total = total_out; cbd->pos = 0; cbd->port = port; cbd->stop_pattern = stop_pattern; if (session) { cbd->session = session; rspamd_session_add_event (session, (event_finalizer_t)lua_tcp_fin, cbd, g_quark_from_static_string ("lua tcp")); } if (rspamd_parse_inet_address (&cbd->addr, host, 0)) { rspamd_inet_address_set_port (cbd->addr, port); /* Host is numeric IP, no need to resolve */ if (!lua_tcp_make_connection (cbd)) { lua_tcp_maybe_free (cbd); lua_pushboolean (L, FALSE); return 1; } } else { if (task == NULL) { if (!make_dns_request (resolver, session, NULL, lua_tcp_dns_handler, cbd, RDNS_REQUEST_A, host)) { lua_tcp_push_error (cbd, "cannot resolve host"); lua_tcp_maybe_free (cbd); } } else { if (!make_dns_request_task (task, lua_tcp_dns_handler, cbd, RDNS_REQUEST_A, host)) { lua_tcp_push_error (cbd, "cannot resolve host"); lua_tcp_maybe_free (cbd); } } } lua_pushboolean (L, TRUE); return 1; }
static gboolean rspamd_redis_try_ucl (struct redis_stat_ctx *backend, const ucl_object_t *obj, struct rspamd_config *cfg, const gchar *symbol) { const ucl_object_t *elt, *relt, *users_enabled; const gchar *lua_script; elt = ucl_object_lookup_any (obj, "read_servers", "servers", NULL); if (elt == NULL) { return FALSE; } backend->read_servers = rspamd_upstreams_create (cfg->ups_ctx); if (!rspamd_upstreams_from_ucl (backend->read_servers, elt, REDIS_DEFAULT_PORT, NULL)) { msg_err ("statfile %s cannot get read servers configuration", symbol); return FALSE; } relt = elt; elt = ucl_object_lookup (obj, "write_servers"); if (elt == NULL) { /* Use read servers as write ones */ g_assert (relt != NULL); backend->write_servers = rspamd_upstreams_create (cfg->ups_ctx); if (!rspamd_upstreams_from_ucl (backend->write_servers, relt, REDIS_DEFAULT_PORT, NULL)) { msg_err ("statfile %s cannot get write servers configuration", symbol); return FALSE; } } else { backend->write_servers = rspamd_upstreams_create (cfg->ups_ctx); if (!rspamd_upstreams_from_ucl (backend->write_servers, elt, REDIS_DEFAULT_PORT, NULL)) { msg_err ("statfile %s cannot get write servers configuration", symbol); rspamd_upstreams_destroy (backend->write_servers); backend->write_servers = NULL; } } elt = ucl_object_lookup (obj, "prefix"); if (elt == NULL || ucl_object_type (elt) != UCL_STRING) { /* Default non-users statistics */ backend->redis_object = REDIS_DEFAULT_OBJECT; /* * Make redis backend compatible with sqlite3 backend in users settings */ users_enabled = ucl_object_lookup_any (obj, "per_user", "users_enabled", NULL); if (users_enabled != NULL) { if (ucl_object_type (users_enabled) == UCL_BOOLEAN) { backend->enable_users = ucl_object_toboolean (users_enabled); backend->cbref_user = -1; if (backend->enable_users) { backend->redis_object = REDIS_DEFAULT_USERS_OBJECT; } } else if (ucl_object_type (users_enabled) == UCL_STRING) { lua_script = ucl_object_tostring (users_enabled); if (luaL_dostring (cfg->lua_state, lua_script) != 0) { msg_err_config ("cannot execute lua script for users " "extraction: %s", lua_tostring (cfg->lua_state, -1)); } else { if (lua_type (cfg->lua_state, -1) == LUA_TFUNCTION) { backend->enable_users = TRUE; backend->cbref_user = luaL_ref (cfg->lua_state, LUA_REGISTRYINDEX); } else { msg_err_config ("lua script must return " "function(task) and not %s", lua_typename (cfg->lua_state, lua_type ( cfg->lua_state, -1))); } } } } else { backend->enable_users = FALSE; } } else { /* XXX: sanity check */ backend->redis_object = ucl_object_tostring (elt); } elt = ucl_object_lookup (obj, "timeout"); if (elt) { backend->timeout = ucl_object_todouble (elt); } else { backend->timeout = REDIS_DEFAULT_TIMEOUT; } elt = ucl_object_lookup (obj, "password"); if (elt) { backend->password = ucl_object_tostring (elt); } else { backend->password = NULL; } elt = ucl_object_lookup_any (obj, "db", "database", "dbname", NULL); if (elt) { backend->dbname = ucl_object_tostring (elt); } else { backend->dbname = NULL; } return TRUE; }
/* Do post load initialization based on lua */ void rspamd_lua_post_load_config (struct rspamd_config *cfg) { lua_State *L = cfg->lua_state; const gchar *name, *val; gchar *sym; struct rspamd_expression *expr, *old_expr; ucl_object_t *obj; gsize keylen; GError *err = NULL; /* First check all module options that may be overriden in 'config' global */ lua_getglobal (L, "config"); if (lua_istable (L, -1)) { /* Iterate */ for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) { /* 'key' is at index -2 and 'value' is at index -1 */ /* Key must be a string and value must be a table */ name = luaL_checklstring (L, -2, &keylen); if (name != NULL && lua_istable (L, -1)) { obj = ucl_object_lua_import (L, lua_gettop (L)); if (obj != NULL) { ucl_object_insert_key_merged (cfg->rcl_obj, obj, name, keylen, true); } } } } /* Check metrics settings */ lua_getglobal (L, "metrics"); if (lua_istable (L, -1)) { /* Iterate */ for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) { /* 'key' is at index -2 and 'value' is at index -1 */ /* Key must be a string and value must be a table */ name = luaL_checkstring (L, -2); if (name != NULL && lua_istable (L, -1)) { lua_process_metric (L, name, cfg); } } } /* Check composites */ lua_getglobal (L, "composites"); if (lua_istable (L, -1)) { /* Iterate */ for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) { /* 'key' is at index -2 and 'value' is at index -1 */ /* Key must be a string and value must be a table */ name = luaL_checkstring (L, -2); if (name != NULL && lua_isstring (L, -1)) { val = lua_tostring (L, -1); sym = rspamd_mempool_strdup (cfg->cfg_pool, name); if (!rspamd_parse_expression (val, 0, &composite_expr_subr, NULL, cfg->cfg_pool, &err, &expr)) { msg_err ("cannot parse composite expression '%s': %s", val, err->message); g_error_free (err); err = NULL; continue; } /* Now check hash table for this composite */ if ((old_expr = g_hash_table_lookup (cfg->composite_symbols, name)) != NULL) { msg_info ("replacing composite symbol %s", name); g_hash_table_replace (cfg->composite_symbols, sym, expr); } else { g_hash_table_insert (cfg->composite_symbols, sym, expr); rspamd_symbols_cache_add_symbol (cfg->cache, sym, 0, NULL, NULL, SYMBOL_TYPE_COMPOSITE, -1); } } } } }
struct rspamd_worker * rspamd_fork_worker (struct rspamd_main *rspamd_main, struct rspamd_worker_conf *cf, guint index, struct event_base *ev_base) { struct rspamd_worker *wrk; gint rc; struct rlimit rlim; /* Starting worker process */ wrk = (struct rspamd_worker *) g_malloc0 (sizeof (struct rspamd_worker)); if (!rspamd_socketpair (wrk->control_pipe)) { msg_err ("socketpair failure: %s", strerror (errno)); exit (-errno); } if (!rspamd_socketpair (wrk->srv_pipe)) { msg_err ("socketpair failure: %s", strerror (errno)); exit (-errno); } wrk->srv = rspamd_main; wrk->type = cf->type; wrk->cf = cf; REF_RETAIN (cf); wrk->index = index; wrk->ctx = cf->ctx; wrk->finish_actions = g_ptr_array_new (); wrk->pid = fork (); switch (wrk->pid) { case 0: /* Update pid for logging */ rspamd_log_update_pid (cf->type, rspamd_main->logger); /* Init PRNG after fork */ rc = ottery_init (rspamd_main->cfg->libs_ctx->ottery_cfg); if (rc != OTTERY_ERR_NONE) { msg_err_main ("cannot initialize PRNG: %d", rc); g_assert (0); } rspamd_random_seed_fast (); g_random_set_seed (ottery_rand_uint32 ()); #ifdef HAVE_EVUTIL_RNG_INIT evutil_secure_rng_init (); #endif /* Remove the inherited event base */ event_reinit (rspamd_main->ev_base); event_base_free (rspamd_main->ev_base); /* Drop privilleges */ rspamd_worker_drop_priv (rspamd_main); /* Set limits */ rspamd_worker_set_limits (rspamd_main, cf); /* Re-set stack limit */ getrlimit (RLIMIT_STACK, &rlim); rlim.rlim_cur = 100 * 1024 * 1024; rlim.rlim_max = rlim.rlim_cur; setrlimit (RLIMIT_STACK, &rlim); setproctitle ("%s process", cf->worker->name); rspamd_pidfile_close (rspamd_main->pfh); /* Do silent log reopen to avoid collisions */ rspamd_log_close (rspamd_main->logger); rspamd_log_open (rspamd_main->logger); wrk->start_time = rspamd_get_calendar_ticks (); #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION <= 30)) # if (GLIB_MINOR_VERSION > 20) /* Ugly hack for old glib */ if (!g_thread_get_initialized ()) { g_thread_init (NULL); } # else g_thread_init (NULL); # endif #endif msg_info_main ("starting %s process %P (%d)", cf->worker->name, getpid (), index); /* Close parent part of socketpair */ close (wrk->control_pipe[0]); close (wrk->srv_pipe[0]); rspamd_socket_nonblocking (wrk->control_pipe[1]); rspamd_socket_nonblocking (wrk->srv_pipe[1]); /* Execute worker */ cf->worker->worker_start_func (wrk); exit (EXIT_FAILURE); break; case -1: msg_err_main ("cannot fork main process. %s", strerror (errno)); rspamd_pidfile_remove (rspamd_main->pfh); exit (-errno); break; default: /* Close worker part of socketpair */ close (wrk->control_pipe[1]); close (wrk->srv_pipe[1]); rspamd_socket_nonblocking (wrk->control_pipe[0]); rspamd_socket_nonblocking (wrk->srv_pipe[0]); rspamd_srv_start_watching (wrk, ev_base); /* Insert worker into worker's table, pid is index */ g_hash_table_insert (rspamd_main->workers, GSIZE_TO_POINTER ( wrk->pid), wrk); break; } return wrk; }
/** * Apply configuration to the specified configuration * @param conf_metrics * @param cfg */ static void apply_dynamic_conf (const ucl_object_t *top, struct rspamd_config *cfg) { gint test_act; const ucl_object_t *cur_elt, *cur_nm, *it_val; ucl_object_iter_t it = NULL; struct metric *real_metric; struct metric_action *cur_action; struct rspamd_symbol_def *s; while ((cur_elt = ucl_iterate_object (top, &it, true))) { if (ucl_object_type (cur_elt) != UCL_OBJECT) { msg_err ("loaded json array element is not an object"); continue; } cur_nm = ucl_object_find_key (cur_elt, "metric"); if (!cur_nm || ucl_object_type (cur_nm) != UCL_STRING) { msg_err ( "loaded json metric object element has no 'metric' attribute"); continue; } real_metric = g_hash_table_lookup (cfg->metrics, ucl_object_tostring (cur_nm)); if (real_metric == NULL) { msg_warn ("cannot find metric %s", ucl_object_tostring (cur_nm)); continue; } cur_nm = ucl_object_find_key (cur_elt, "symbols"); /* Parse symbols */ if (cur_nm && ucl_object_type (cur_nm) == UCL_ARRAY) { ucl_object_iter_t nit = NULL; while ((it_val = ucl_iterate_object (cur_nm, &nit, true))) { if (ucl_object_find_key (it_val, "name") && ucl_object_find_key (it_val, "value")) { const ucl_object_t *n = ucl_object_find_key (it_val, "name"); const ucl_object_t *v = ucl_object_find_key (it_val, "value"); if((s = g_hash_table_lookup (real_metric->symbols, ucl_object_tostring (n))) != NULL) { *s->weight_ptr = ucl_object_todouble (v); } } else { msg_info ( "json symbol object has no mandatory 'name' and 'value' attributes"); } } } else { ucl_object_t *arr; arr = ucl_object_typed_new (UCL_ARRAY); ucl_object_insert_key ((ucl_object_t *)cur_elt, arr, "symbols", sizeof ("symbols") - 1, false); } cur_nm = ucl_object_find_key (cur_elt, "actions"); /* Parse actions */ if (cur_nm && ucl_object_type (cur_nm) == UCL_ARRAY) { ucl_object_iter_t nit = NULL; while ((it_val = ucl_iterate_object (cur_nm, &nit, true))) { if (ucl_object_find_key (it_val, "name") && ucl_object_find_key (it_val, "value")) { if (!rspamd_action_from_str (ucl_object_tostring ( ucl_object_find_key (it_val, "name")), &test_act)) { msg_err ("unknown action: %s", ucl_object_tostring (ucl_object_find_key (it_val, "name"))); continue; } cur_action = &real_metric->actions[test_act]; cur_action->action = test_act; cur_action->score = ucl_object_todouble (ucl_object_find_key (it_val, "value")); } else { msg_info ( "json action object has no mandatory 'name' and 'value' attributes"); } } } else { ucl_object_t *arr; arr = ucl_object_typed_new (UCL_ARRAY); ucl_object_insert_key ((ucl_object_t *)cur_elt, arr, "actions", sizeof ("actions") - 1, false); } } }
/* * nvram_readwrite() * Read the contents of NVRAM */ void nvram_readwrite(struct msg *m, struct file *f) { int tsfr_count, ret = 0; uchar tbyte; /* * Check that the transfer is sane */ if (m->m_arg == 0) { msg_err(m->m_sender, EINVAL); return; } /* * Check that the permissions are good */ if ((m->m_op == FS_WRITE) ? (!(f->f_flags & ACC_WRITE)) : (!(f->f_flags & ACC_READ))) { msg_err(m->m_sender, EPERM); return; } /* * How many bytes are we going to transfer? */ tsfr_count = m->m_arg; /* * Read/write the NVRAM via the transfer buffer. We assume * here that on * exit from whichever the appropriate * routine is we return a status in * "ret". If ret is * non-zero we have a fail, otherwise we have a pass. */ if (m->m_op == FS_READ) { /* * Handle device reads */ switch(f->f_node) { case ROOT_INO : nvram_readdir(m, f); ret = 0; break; case ALL_INO : CAP_TSFR(tsfr_count, f->f_pos, nvram_bytes); ret = do_rw_all(f->f_pos, tsfr_count, nvram_buffer, READING); break; case RTC_INO : CAP_TSFR(tsfr_count, f->f_pos, 7); ret = do_rw_rtc(f->f_pos, tsfr_count, nvram_buffer, READING); break; case FD0_INO : CAP_TSFR(tsfr_count, f->f_pos, 1); ret = do_readbyte(0x10, nvram_buffer); nvram_buffer[0] >>= 4; break; case FD1_INO : CAP_TSFR(tsfr_count, f->f_pos, 1); ret = do_readbyte(0x10, nvram_buffer); nvram_buffer[0] &= 0x0f; break; case WD0_INO : CAP_TSFR(tsfr_count, f->f_pos, 1); ret = do_readbyte(0x12, nvram_buffer); nvram_buffer[0] >>= 4; if (nvram_buffer[0] == 0x0f) { ret = do_readbyte(0x19, nvram_buffer); } break; case WD1_INO : CAP_TSFR(tsfr_count, f->f_pos, 1); ret = do_readbyte(0x12, nvram_buffer); nvram_buffer[0] &= 0x0f; if (nvram_buffer[0] == 0x0f) { ret = do_readbyte(0x1a, nvram_buffer); } break; default : msg_err(m->m_sender, EINVAL); return; } } else { /* * Handle device writes */ switch(f->f_node) {
/* * tmpfs_read() * Read bytes out of the current file or directory * * Directories get their own routine. */ void tmpfs_read(struct msg *m, struct file *f) { uint nseg, cnt, lim, blk; struct openfile *o; char *blkp, *tmpbuf = NULL; seg_t *mp; /* * Directory--only one is the root */ if ((o = f->f_file) == 0) { tmpfs_readdir(m, f); return; } /* * Access? */ if (!(f->f_perm & ACC_READ)) { msg_err(m->m_sender, EPERM); return; } /* * EOF? */ if (f->f_pos >= o->o_len) { m->m_arg = m->m_arg1 = m->m_buflen = m->m_nseg = 0; msg_reply(m->m_sender, m); return; } /* * Calculate # bytes to get */ lim = m->m_arg; if (lim > (o->o_len - f->f_pos)) { lim = o->o_len - f->f_pos; } /* * Build message segments */ cnt = 0; for (nseg = 0; (cnt < lim) && (nseg < MSGSEGS); ++nseg) { uint off, sz; /* * Get next block of data. We simulate sparse * files by using our pre-allocated source of * zeroes. */ blk = blknum(f->f_pos); blkp = hash_lookup(o->o_blocks, blk); if (blkp == 0) { extern char *zeroes; blkp = zeroes; } /* * Calculate how much of the block to add to * the message. */ blkoff(f->f_pos, &off, &sz); if ((cnt+sz) > lim) { sz = lim - cnt; } /* * Put into next message segment */ m->m_seg[nseg].s_buf = blkp+off; m->m_seg[nseg].s_buflen = sz; /* * Advance counter */ cnt += sz; f->f_pos += sz; } /* * If we couldn't satisfy the read using our scatter/gather * allowance of buffers, use the last slot as a pointer to * a buffer which will hold all the necessary data. */ if (cnt < lim) { uint tmpoff = 0; /* * Get a buffer which can hold the last data of * the message segments already assembled, plus * all the data we haven't read in yet. */ mp = &m->m_seg[MSGSEGS-1]; tmpoff = mp->s_buflen; tmpbuf = malloc(tmpoff + (lim - cnt)); if (tmpbuf == NULL) { goto retshort; } /* * Copy in the current data */ bcopy(mp->s_buf, tmpbuf, tmpoff); /* * Now bring in the rest of the data from the file */ while (cnt < lim) { uint off, sz; /* * Get next block of data. We simulate sparse * files by using our pre-allocated source of * zeroes. */ blk = blknum(f->f_pos); blkp = hash_lookup(o->o_blocks, blk); if (blkp == 0) { extern char *zeroes; blkp = zeroes; } /* * Calculate how much of the block to add to * the message. */ blkoff(f->f_pos, &off, &sz); if ((cnt+sz) > lim) { sz = lim - cnt; } /* * Put into next message segment */ bcopy(blkp+off, tmpbuf+tmpoff, sz); /* * Advance counter */ cnt += sz; f->f_pos += sz; tmpoff += sz; } /* * Point the last message segment into this buffer */ mp->s_buf = tmpbuf; mp->s_buflen = tmpoff; } /* * Send back reply */ retshort: m->m_arg = cnt; m->m_nseg = nseg; m->m_arg1 = 0; msg_reply(m->m_sender, m); /* * Free our extra buffer if it's been used */ if (tmpbuf) { free(tmpbuf); } }
/* * rspamd_dispatcher.create(base,fd, read_cb, write_cb, err_cb[, timeout]) */ static int lua_io_dispatcher_create (lua_State *L) { struct rspamd_io_dispatcher_s *io_dispatcher, **pdispatcher; gint fd; struct lua_dispatcher_cbdata *cbdata; struct timeval tv = {0, 0}; double tv_num, tmp; if (lua_gettop (L) >= 5 && lua_isfunction (L, 3) && lua_isfunction (L, 5)) { cbdata = g_slice_alloc0 (sizeof (struct lua_dispatcher_cbdata)); cbdata->base = lua_check_event_base (L); if (cbdata->base == NULL) { /* Create new event base */ msg_warn ("create new event base as it is not specified"); cbdata->base = event_init (); } cbdata->L = L; fd = lua_tointeger (L, 2); lua_pushvalue (L, 3); cbdata->cbref_read = luaL_ref (L, LUA_REGISTRYINDEX); if (lua_isfunction (L, 4)) { /* Push write callback as well */ lua_pushvalue (L, 4); cbdata->cbref_write = luaL_ref (L, LUA_REGISTRYINDEX); } /* Error callback */ lua_pushvalue (L, 5); cbdata->cbref_err = luaL_ref (L, LUA_REGISTRYINDEX); if (lua_gettop (L) > 5) { tv_num = lua_tonumber (L, 6); tv.tv_sec = trunc (tv_num); tv.tv_usec = modf (tv_num, &tmp) * 1000.; io_dispatcher = rspamd_create_dispatcher (cbdata->base, fd, BUFFER_LINE, lua_io_read_cb, lua_io_write_cb, lua_io_err_cb, &tv, cbdata); } else { io_dispatcher = rspamd_create_dispatcher (cbdata->base, fd, BUFFER_LINE, lua_io_read_cb, lua_io_write_cb, lua_io_err_cb, NULL, cbdata); } cbdata->d = io_dispatcher; /* Push result */ pdispatcher = lua_newuserdata (L, sizeof (struct rspamd_io_dispatcher_s *)); rspamd_lua_setclass (L, "rspamd{io_dispatcher}", -1); *pdispatcher = io_dispatcher; } else { msg_err ("invalid number of arguments to io_dispatcher.create: %d", lua_gettop (L)); lua_pushnil (L); } return 1; }
/***************************************************************************** * Reads configuration file. *****************************************************************************/ void read_config_file() { char line[INPUT_LINE_MAXLEN + 1], *p, *q, *r; int f_line_too_long, f_used, line_number, i; char var[VAR_MAXLEN + 1], *var_b, *var_e; char command[SHELL_COMMAND_MAXLEN + 1]; uint32_t ip; struct in_addr in_addr; uint16_t port; uint8_t f_unixsock; FILE *f; char ipv6_any[] = "::"; /* set default values */ conf.apache_count = 0; conf.nginx_count = 0; conf.memcache_count = 0; conf.socket_count = 0; conf.socket_interval = 0; conf.exec_count = 0; /* open config file */ if ((f = fopen(conf.configfile, "r")) == NULL) { msg_syswarn("%s: fopen(%s)", __FUNCTION__, conf.configfile); return; } f_line_too_long = 0; line_number = 0; while (fgets(line, sizeof(line), f)) { /* remove end of line for easy parsing */ parse_chomp(line); /* line too long, ignoring */ if (strlen(line) == (sizeof(line) - 1)) { f_line_too_long = 1; continue; } line_number++; /* skip the rest of too long line */ if (f_line_too_long) { f_line_too_long = 0; msg_err(0, "%s: line %d: line is too long", __FUNCTION__, line_number); continue; } /* remove trailing white spaces for easy parsing */ parse_rtrim(line); /* do parsing */ /* empty lines and comments */ if (!*line || parse_get_ch(line, &p, '#')) { continue; } else if (parse_get_str(line, &p, "apache")) { /* format: apache <variable> <ip> <port> */ if (parse_get_wspace(p, &var_b) && parse_get_chset(var_b, &var_e, VAR_CHSET, -(int)(sizeof(var) - 1)) && parse_get_wspace(var_e, &p) && parse_get_ip4(p, &p, &ip) && parse_get_wspace(p, &p) && parse_get_uint16(p, &p, &port) && !*p) { strncpy(var, var_b, var_e - var_b); var[var_e - var_b] = 0; parse_tolower(var); /* check if variable is already used */ f_used = 0; for (i = 0; i < conf.apache_count; i++) if (strcmp(conf.apache_conf[i].var, var) == 0) { f_used = 1; break; } if (f_used) { msg_err(0, "%s: line %d: dublicated variable '%s'", __FUNCTION__, line_number, var); continue; } /* check if too many apache directives */ if (conf.apache_count == APACHE_MAXN) { msg_err(0, "%s: line %d: too many 'apache' directives (maximum %d allowed)", __FUNCTION__, line_number, APACHE_MAXN); continue; } /* add line to apache configuration */ strcpy(conf.apache_conf[conf.apache_count].var, var); conf.apache_conf[conf.apache_count].ip = ip; in_addr.s_addr = ip; strcpy(conf.apache_conf[conf.apache_count].ip_str, inet_ntoa(in_addr)); conf.apache_conf[conf.apache_count].port = port; conf.apache_count++; } else msg_err(0, "%s: line %d: can't parse 'apache' directive", __FUNCTION__, line_number); } else if (parse_get_str(line, &p, "nginx")) { /* format: nginx <variable> <ip> <port> */ if (parse_get_wspace(p, &var_b) && parse_get_chset(var_b, &var_e, VAR_CHSET, -(int)(sizeof(var) - 1)) && parse_get_wspace(var_e, &p) && parse_get_ip4(p, &p, &ip) && parse_get_wspace(p, &p) && parse_get_uint16(p, &p, &port) && !*p) { strncpy(var, var_b, var_e - var_b); var[var_e - var_b] = 0; parse_tolower(var); /* check if variable is already used */ f_used = 0; for (i = 0; i < conf.nginx_count; i++) if (strcmp(conf.nginx_conf[i].var, var) == 0) { f_used = 1; break; } if (f_used) { msg_err(0, "%s: line %d: dublicated variable '%s'", __FUNCTION__, line_number, var); continue; } /* check if too many nginx directives */ if (conf.nginx_count == NGINX_MAXN) { msg_err(0, "%s: line %d: too many 'nginx' directives (maximum %d allowed)", __FUNCTION__, line_number, NGINX_MAXN); continue; } /* add line to nginx configuration */ strcpy(conf.nginx_conf[conf.nginx_count].var, var); conf.nginx_conf[conf.nginx_count].ip = ip; in_addr.s_addr = ip; strcpy(conf.nginx_conf[conf.nginx_count].ip_str, inet_ntoa(in_addr)); conf.nginx_conf[conf.nginx_count].port = port; conf.nginx_count++; } else msg_err(0, "%s: line %d: can't parse 'nginx' directive", __FUNCTION__, line_number); } else if (parse_get_str(line, &p, "memcache")) { /* format 1: memcache <variable> <ip> <port> */ /* format 2: memcache <variable> <sockname> */ if (parse_get_wspace(p, &var_b) && parse_get_chset(var_b, &var_e, VAR_CHSET, -(int)(sizeof(var) - 1)) && parse_get_wspace(var_e, &p) && ((parse_get_ip4(p, &q, &ip) && parse_get_wspace(q, &q) && parse_get_uint16(q, &q, &port) && (f_unixsock = 0, 1)) || (parse_get_chset(p, &q, "^ \t", -SOCKNAME_MAXLEN) && (f_unixsock = 1, 1))) && !*q) { strncpy(var, var_b, var_e - var_b); var[var_e - var_b] = 0; parse_tolower(var); /* check if variable is already used */ f_used = 0; for (i = 0; i < conf.memcache_count; i++) if (strcmp(conf.memcache_conf[i].var, var) == 0) { f_used = 1; break; } if (f_used) { msg_err(0, "%s: line %d: dublicated variable '%s'", __FUNCTION__, line_number, var); continue; } /* check if too many memcache directives */ if (conf.memcache_count == MEMCACHE_MAXN) { msg_err(0, "%s: line %d: too many 'memcache' directives (maximum %d allowed)", __FUNCTION__, line_number, MEMCACHE_MAXN); continue; } /* add line to memcache configuration */ strcpy(conf.memcache_conf[conf.memcache_count].var, var); conf.memcache_conf[conf.memcache_count].f_unixsock = f_unixsock; if (f_unixsock) { strncpy(conf.memcache_conf[conf.memcache_count].sockname, p, q - p); conf.memcache_conf[conf.memcache_count].sockname[q - p] = 0; } else { conf.memcache_conf[conf.memcache_count].ip = ip; conf.memcache_conf[conf.memcache_count].port = port; } conf.memcache_count++; } else msg_err(0, "%s: line %d: can't parse 'memcache' directive", __FUNCTION__, line_number); } else if (parse_get_str(line, &p, "socket")) { /* format 1: socket tcp|udp <variable> <ip> <port> */ /* format 2: socket (tcp|udp)6 <variable> <ip6> <port> */ /* format 3: socket unix <variable> <path> */ if (parse_get_wspace(p, &var_b) && parse_get_chset(var_b, &var_e, VAR_CHSET, -(int)(sizeof(var) - 1)) && parse_get_wspace(var_e, &p) && ( (((parse_get_str(p, &q, "tcp") && (f_unixsock = 0, 1)) || (parse_get_str(p, &q, "udp") && (f_unixsock = 1, 1))) && parse_get_wspace(q, &q) && (parse_get_ip4(q, &q, &ip) || (parse_get_ch(q, &q, '*') && (ip = 0, 1))) && parse_get_wspace(q, &q) && parse_get_uint16(q, &q, &port)) || (((parse_get_str(p, &q, "tcp6") && (f_unixsock = 2, 1)) || (parse_get_str(p, &q, "udp6") && (f_unixsock = 3, 1))) && parse_get_wspace(q, &p) && (parse_get_chset(p, &r, "0123456789aAbBcCdDeEfF:.", -64) || (parse_get_ch(p, &r, '*') && (p = ipv6_any, 1))) && parse_get_wspace(r, &q) && parse_get_uint16(q, &q, &port)) || (parse_get_str(p, &r, "unix") && (f_unixsock = 4, 1) && parse_get_wspace(r, &r) && parse_get_chset(r, &q, "^ \t", -SOCKNAME_MAXLEN))) && !*q) { strncpy(var, var_b, var_e - var_b); var[var_e - var_b] = 0; parse_tolower(var); /* check if variable is already used */ f_used = 0; for (i = 0; i < conf.socket_count; i++) if(strcmp(conf.socket_conf[i].var, var) == 0) { f_used = 1; break; } if (f_used) { msg_err(0, "%s: line %d: dublicated variable '%s'", __FUNCTION__, line_number, var); continue; } /* check if too many socket directives */ if (conf.socket_count == SOCKET_MAXN) { msg_err(0, "%s: line %d: too many 'socket' directives (maximum %d allowed)", __FUNCTION__, line_number, SOCKET_MAXN); continue; } /* add line to socket configuration */ strcpy(conf.socket_conf[conf.socket_count].var, var); conf.socket_conf[conf.socket_count].type = f_unixsock; bzero(&conf.socket_conf[conf.socket_count].sockaddr, sizeof(conf.socket_conf[conf.socket_count].sockaddr)); if (f_unixsock == 4) { conf.socket_conf[conf.socket_count].sockaddr.sun.sun_family = AF_LOCAL; strncpy(conf.socket_conf[conf.socket_count].sockaddr.sun.sun_path, r, q - r); conf.socket_conf[conf.socket_count].sockaddr.sun.sun_path[q - p] = 0; } else if (f_unixsock == 0 || f_unixsock == 1) { conf.socket_conf[conf.socket_count].sockaddr.sin.sin_family = AF_INET; conf.socket_conf[conf.socket_count].sockaddr.sin.sin_port = htons(port); conf.socket_conf[conf.socket_count].sockaddr.sin.sin_addr.s_addr = ip; } else { conf.socket_conf[conf.socket_count].sockaddr.sin6.sin6_family = AF_INET6; conf.socket_conf[conf.socket_count].sockaddr.sin6.sin6_port = htons(port); *r = 0; if (inet_pton(AF_INET6, p, &conf.socket_conf[conf.socket_count].sockaddr.sin6.sin6_addr) != 1) { msg_err(0, "%s: line %d: can't parse ipv6 addr %s", __FUNCTION__, line_number, p); continue; } } conf.socket_count++; } else msg_err(0, "%s: line %d: can't parse 'socket' directive, error at (%d) ^%s, q=%s, r=%s", __FUNCTION__, line_number, q-p, p, q, r); } else if (parse_get_str(line, &p, "exec")) { /* format: exec <command> */ if (parse_get_wspace(p, &p) && (strlen(p) < sizeof(command))) { strcpy(command, p); /* check if too many exec directives */ if (conf.exec_count == EXEC_MAXN) { msg_err(0, "%s: line %d: too many 'exec' directives (maximum %d allowed)", __FUNCTION__, line_number, EXEC_MAXN); continue; } /* add line to exec configuration */ strcpy(conf.exec_conf[conf.exec_count].command, command); conf.exec_count++; } else msg_err(0, "%s: line %d: can't parse 'exec' directive", __FUNCTION__, line_number); } else if (parse_get_str(line, &p, "sock_la_interval")) { /* format: sock_la_interval <seconds> */ if (!parse_get_wspace(p, &p) || !parse_get_int(p, &p, &conf.socket_interval) || (*p)) msg_err(0, "%s: line %d: can't parse 'sock_la_interval' directive", __FUNCTION__, line_number); } else if (parse_get_str(line, &p, "no_smart_enable")) { if (!*p) { conf.f_enable_smart = 0; } else msg_err(0, "%s: line %d: can't parse 'no_smart_enable' directive", __FUNCTION__, line_number); } else if (parse_get_str(line, &p, "no_hdds_la")) { if (!*p) { conf.f_disable_hdds_la = 1; } else msg_err(0, "%s: line %d: can't parse 'no_hdds_la' directive", __FUNCTION__, line_number); } else if (parse_get_str(line, &p, "no_p2p_interfaces")) { if (!*p) { conf.f_skip_p2p_interfaces = 1; } else msg_err(0, "%s: line %d: can't parse 'no_p2p_interfaces' directive", __FUNCTION__, line_number); } else { msg_err(0, "%s: line %d: can't parse line", __FUNCTION__, line_number); } } if (ferror(f)) { msg_syserr(0, "%s: fgets", __FUNCTION__); fclose(f); return; } fclose(f); if (f_started) msg_info("configuration file processed"); }
/* * tmpfs_readdir() * Do reads on directory entries */ static void tmpfs_readdir(struct msg *m, struct file *f) { char *buf; uint len, pos, bufcnt; struct llist *l; struct openfile *o; extern struct llist files; /* * Get a buffer of the requested size, but put a sanity * cap on it. */ len = m->m_arg; if (len > 256) { len = 256; } if ((buf = malloc(len+1)) == 0) { msg_err(m->m_sender, strerror()); return; } buf[0] = '\0'; /* * Assemble as many names as will fit, starting at * given byte offset. We assume the caller's position * always advances in units of a whole directory entry. */ bufcnt = pos = 0; for (l = LL_NEXT(&files); l != &files; l = LL_NEXT(l)) { uint slen; /* * Point to next file. Get its length. */ o = l->l_data; slen = strlen(o->o_name)+1; /* * If we've reached an offset the caller hasn't seen * yet, assemble the entry into the buffer. */ if (pos >= f->f_pos) { /* * No more room in buffer--return results */ if (slen >= len) { break; } /* * Put string with newline at end of buffer */ sprintf(buf + bufcnt, "%s\n", o->o_name); /* * Update counters */ len -= slen; bufcnt += slen; } /* * Update position */ pos += slen; } /* * Send back results */ m->m_buf = buf; m->m_arg = m->m_buflen = bufcnt; m->m_nseg = ((bufcnt > 0) ? 1 : 0); m->m_arg1 = 0; msg_reply(m->m_sender, m); free(buf); f->f_pos = pos; }
gboolean rspamd_symbols_cache_validate (struct symbols_cache *cache, struct rspamd_config *cfg, gboolean strict) { struct cache_item *item; GHashTableIter it; GList *cur; gpointer k, v; struct rspamd_symbol_def *sym_def; struct metric *metric; gboolean ignore_symbol = FALSE, ret = TRUE; if (cache == NULL) { msg_err ("empty cache is invalid"); return FALSE; } /* Now adjust symbol weights according to default metric */ if (cfg->default_metric != NULL) { g_hash_table_foreach (cfg->default_metric->symbols, rspamd_symbols_cache_metric_validate_cb, cache); } g_hash_table_foreach (cache->items_by_symbol, rspamd_symbols_cache_validate_cb, cache); /* Now check each metric item and find corresponding symbol in a cache */ g_hash_table_iter_init (&it, cfg->metrics_symbols); while (g_hash_table_iter_next (&it, &k, &v)) { ignore_symbol = FALSE; cur = v; while (cur) { metric = cur->data; sym_def = g_hash_table_lookup (metric->symbols, k); if (sym_def && (sym_def->flags & RSPAMD_SYMBOL_FLAG_IGNORE)) { ignore_symbol = TRUE; break; } cur = g_list_next (cur); } if (!ignore_symbol) { item = g_hash_table_lookup (cache->items_by_symbol, k); if (item == NULL) { msg_warn_cache ( "symbol '%s' has its score defined but there is no " "corresponding rule registered", k); if (strict) { ret = FALSE; } } } } return ret; }
void rspamd_log_close_priv (rspamd_logger_t *rspamd_log, uid_t uid, gid_t gid) { gchar tmpbuf[256]; rspamd_log_flush (rspamd_log); switch (rspamd_log->type) { case RSPAMD_LOG_CONSOLE: /* Do nothing special */ break; case RSPAMD_LOG_SYSLOG: #ifdef HAVE_SYSLOG_H closelog (); #endif break; case RSPAMD_LOG_FILE: if (rspamd_log->enabled) { if (rspamd_log->repeats > REPEATS_MIN) { rspamd_snprintf (tmpbuf, sizeof (tmpbuf), "Last message repeated %ud times", rspamd_log->repeats); rspamd_log->repeats = 0; if (rspamd_log->saved_message) { file_log_function (NULL, rspamd_log->saved_module, rspamd_log->saved_id, rspamd_log->saved_function, rspamd_log->cfg->log_level, rspamd_log->saved_message, TRUE, rspamd_log); g_free (rspamd_log->saved_message); g_free (rspamd_log->saved_function); g_free (rspamd_log->saved_module); g_free (rspamd_log->saved_id); rspamd_log->saved_message = NULL; rspamd_log->saved_function = NULL; rspamd_log->saved_module = NULL; rspamd_log->saved_id = NULL; } /* It is safe to use temporary buffer here as it is not static */ file_log_function (NULL, NULL, NULL, G_STRFUNC, rspamd_log->cfg->log_level, tmpbuf, TRUE, rspamd_log); return; } if (fsync (rspamd_log->fd) == -1) { msg_err ("error syncing log file: %s", strerror (errno)); } close (rspamd_log->fd); } break; } rspamd_log->enabled = FALSE; }
/* * nvram_main() * Endless loop to receive and serve requests */ static void nvram_main() { struct msg msg; int x; struct file *f; loop: /* * Receive a message, log an error and then keep going */ x = msg_receive(nvram_port, &msg); if (x < 0) { syslog(LOG_ERR, "msg_receive"); goto loop; } /* * All incoming data should fit in one buffer */ if (msg.m_nseg > 1) { msg_err(msg.m_sender, EINVAL); goto loop; } /* * Categorize by basic message operation */ f = hash_lookup(filehash, msg.m_sender); msg.m_op &= MSG_MASK; switch (msg.m_op) { case M_CONNECT: /* New client */ nvram_new_client(&msg); break; case M_DISCONNECT: /* Client done */ nvram_dead_client(&msg, f); break; case M_DUP: /* File handle dup during exec() */ nvram_dup_client(&msg, f); break; case M_ABORT: /* Aborted operation */ /* * We're synchronous, so presumably the operation is * done and this abort is old news */ msg_reply(msg.m_sender, &msg); break; case FS_SEEK: /* Set position */ if (!f || (msg.m_arg < 0)) { msg_err(msg.m_sender, EINVAL); break; } f->f_pos = msg.m_arg; msg.m_arg = msg.m_arg1 = msg.m_nseg = 0; msg_reply(msg.m_sender, &msg); break; case FS_ABSREAD: /* Set position, then read */ case FS_ABSWRITE: /* Set position, then write */ if (!f || (msg.m_arg1 < 0)) { msg_err(msg.m_sender, EINVAL); break; } f->f_pos = msg.m_arg1; msg.m_op = (msg.m_op == FS_ABSREAD) ? FS_READ : FS_WRITE; /* * Now fall into FS_READ/FS_WRITE */ case FS_READ: /* Read file */ case FS_WRITE: /* Write file */ if (nvram_check_gen(&msg, f)) { break; } nvram_readwrite(&msg, f); break; case FS_STAT: /* Get stat of file */ if (nvram_check_gen(&msg, f)) { break; } nvram_stat(&msg, f); break; case FS_WSTAT: /* Writes stats */ if (nvram_check_gen(&msg, f)) { break; } nvram_wstat(&msg, f); break; case FS_OPEN: /* Move from dir down into drive */ if (!valid_fname(msg.m_buf, x)) { msg_err(msg.m_sender, EINVAL); break; } nvram_open(&msg, f); break; default: /* Unknown */ msg_err(msg.m_sender, EINVAL); break; } goto loop; }
void json_config_fin_cb (rspamd_mempool_t * pool, struct map_cb_data *data) { struct config_json_buf *jb; ucl_object_t *top; struct ucl_parser *parser; if (data->prev_data) { jb = data->prev_data; /* Clean prev data */ if (jb->buf) { g_free (jb->buf); } g_free (jb); } /* Now parse json */ if (data->cur_data) { jb = data->cur_data; } else { msg_err ("no data read"); return; } if (jb->buf == NULL) { msg_err ("no data read"); return; } /* NULL terminate current buf */ *jb->pos = '\0'; parser = ucl_parser_new (0); if (!ucl_parser_add_chunk (parser, jb->buf, jb->pos - jb->buf)) { msg_err ("cannot load json data: parse error %s", ucl_parser_get_error (parser)); ucl_parser_free (parser); return; } top = ucl_parser_get_object (parser); ucl_parser_free (parser); if (ucl_object_type (top) != UCL_ARRAY) { ucl_object_unref (top); msg_err ("loaded json is not an array"); return; } jb->cfg->current_dynamic_conf = NULL; ucl_object_unref (jb->obj); jb->obj = top; /* * Note about thread safety: we are updating values that are gdoubles so it is not atomic in general case * but on the other hand all that data is used only in the main thread, so why it is *likely* safe * to do this task in this way without explicit lock. */ apply_dynamic_conf (jb->obj, jb->cfg); jb->cfg->current_dynamic_conf = jb->obj; }
/* Fd must be opened for writing, after creating file is mmapped */ static gboolean create_cache_file (struct symbols_cache *cache, const gchar *filename, gint fd, rspamd_mempool_t *pool) { GChecksum *cksum; u_char *digest; gsize cklen; GList *cur; struct cache_item *item; /* Calculate checksum */ cksum = get_mem_cksum (cache); if (cksum == NULL) { msg_err ("cannot calculate checksum for symbols"); close (fd); return FALSE; } cklen = g_checksum_type_get_length (G_CHECKSUM_SHA1); digest = g_malloc (cklen); g_checksum_get_digest (cksum, digest, &cklen); /* Now write data to file */ cur = g_list_first (cache->negative_items); while (cur) { item = cur->data; if (write (fd, item->s, sizeof (struct saved_cache_item)) == -1) { msg_err ("cannot write to file %d, %s", errno, strerror (errno)); close (fd); g_checksum_free (cksum); g_free (digest); return FALSE; } cur = g_list_next (cur); } cur = g_list_first (cache->static_items); while (cur) { item = cur->data; if (write (fd, item->s, sizeof (struct saved_cache_item)) == -1) { msg_err ("cannot write to file %d, %s", errno, strerror (errno)); close (fd); g_checksum_free (cksum); g_free (digest); return FALSE; } cur = g_list_next (cur); } /* Write checksum */ if (write (fd, digest, cklen) == -1) { msg_err ("cannot write to file %d, %s", errno, strerror (errno)); close (fd); g_checksum_free (cksum); g_free (digest); return FALSE; } close (fd); g_checksum_free (cksum); g_free (digest); /* Reopen for reading */ if ((fd = open (filename, O_RDWR)) == -1) { msg_info ("cannot open file %s, error %d, %s", errno, strerror (errno)); return FALSE; } return mmap_cache_file (cache, fd, pool); }
/** * Dump dynamic configuration to the disk * @param cfg * @return */ gboolean dump_dynamic_config (struct rspamd_config *cfg) { struct stat st; gchar *dir, pathbuf[PATH_MAX]; gint fd; if (cfg->dynamic_conf == NULL || cfg->current_dynamic_conf == NULL) { /* No dynamic conf has been specified, so do not try to dump it */ return FALSE; } dir = g_path_get_dirname (cfg->dynamic_conf); if (dir == NULL) { msg_err ("invalid path: %s", cfg->dynamic_conf); return FALSE; } if (stat (cfg->dynamic_conf, &st) == -1) { msg_debug ("%s is unavailable: %s", cfg->dynamic_conf, strerror (errno)); st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; } if (access (dir, W_OK | R_OK) == -1) { msg_warn ("%s is inaccessible: %s", dir, strerror (errno)); g_free (dir); return FALSE; } rspamd_snprintf (pathbuf, sizeof (pathbuf), "%s%crconf-XXXXXX", dir, G_DIR_SEPARATOR); g_free (dir); #ifdef HAVE_MKSTEMP /* Umask is set before */ fd = mkstemp (pathbuf); #else fd = g_mkstemp_full (pathbuf, O_RDWR, S_IWUSR | S_IRUSR); #endif if (fd == -1) { msg_err ("mkstemp error: %s", strerror (errno)); return FALSE; } if (!ucl_object_emit_full (cfg->current_dynamic_conf, UCL_EMIT_JSON, ucl_object_emit_fd_funcs (fd))) { msg_err ("cannot emit ucl object: %s", strerror (errno)); close (fd); return FALSE; } (void)unlink (cfg->dynamic_conf); /* Rename old config */ if (rename (pathbuf, cfg->dynamic_conf) == -1) { msg_err ("rename error: %s", strerror (errno)); close (fd); unlink (pathbuf); return FALSE; } /* Set permissions */ if (chmod (cfg->dynamic_conf, st.st_mode) == -1) { msg_warn ("chmod failed: %s", strerror (errno)); } close (fd); return TRUE; }
/* * fd_wstat() * Allow writing of supported stat messages */ void fd_wstat(struct msg *m, struct file *f) { char *field, *val; struct floppy *fl; fl = unit(f->f_unit); /* * See if common handling code can do it */ if (do_wstat(m, &fd_prot, f->f_flags, &field, &val) == 0) { return; } /* * Process each of the fields we handle specially here */ if (!strcmp(field, "mediachg") && (f->f_slot != ROOTDIR)) { /* * Number of times we've detected media changes */ int chg; if (val) { chg = atoi(val); } else { chg = 0; } fl->f_mediachg = chg; } else if (!strcmp(field, "retries")) { /* * Number of times we attempt to retry an operation */ int retr, x; if (val) { retr = atoi(val); } else { retr = FD_MAXERR; } if (f->f_slot == ROOTDIR) { /* * Set the global retry count */ fd_retries = retr; for (x = 0; x < NFD; x++) { if (floppies[x].f_state != F_NXIO) { floppies[x].f_retries = retr; } } } else { /* * Set the specific drive's retry count */ fl->f_retries = retr; } } else if (!strcmp(field, "messages")) { /* * When will we issue syslog messages? */ int x, fdm = -1; if (val) { for (fdm = FDM_ALL; fdm <= FDM_CRITICAL + 1; fdm++) { if (fdm > FDM_CRITICAL) { /* * Ehh? */ msg_err(m->m_sender, EINVAL); return; } if (!strcmp(val, fdm_opts[fdm])) { break; } } } else { fdm = FDM_FAIL; } if (f->f_slot == ROOTDIR) { /* * Set the global messaging level */ fd_messages = fdm; for (x = 0; x < NFD; x++) { if (floppies[x].f_state != F_NXIO) { floppies[x].f_messages = fdm; } } } else { /* * Set the specific drive's messaging level */ fl->f_messages = fdm; } } else if (!strcmp(field, "findparms") && (f->f_slot == SPECIALNODE)) { /* * How are we going to determine the special node's diskette * media parameters? */ if (!strcmp(val, "auto")) { /* * Selected autoprobing of the media details */ fl->f_density = fl->f_specialdens = DISK_AUTOPROBE; fl->f_parms.f_size = FD_PUNDEF; } else if (!strcmp(val, "userdef")) { /* * Selected user defined parameters */ fl->f_density = fl->f_specialdens = DISK_USERDEF; fl->f_parms = fl->f_userp; } else { /* * We don't understand that option */ msg_err(m->m_sender, EINVAL); return; } } else if (!strcmp(field, "parms") && (f->f_slot == SPECIALNODE)) { /* * What parameters would the user like? */ if (!strcmp(val, "undefined")) { /* * None at all... effectively disable device */ fl->f_parms.f_size = FD_PUNDEF; } else { if (!scan_parms(val, &fl->f_userp)) { msg_err(m->m_sender, EINVAL); return; } if (fl->f_density == DISK_USERDEF) { fl->f_parms = fl->f_userp; } } } else { /* * Not a message we understand */ msg_err(m->m_sender, EINVAL); return; } /* * Return success */ m->m_buflen = m->m_nseg = m->m_arg = m->m_arg1 = 0; msg_reply(m->m_sender, m); }
static void rspamadm_statconvert (gint argc, gchar **argv, const struct rspamadm_command *cmd) { GOptionContext *context; GError *error = NULL; ucl_object_t *obj; context = g_option_context_new ( "statconvert - converts statistics from sqlite3 to redis"); g_option_context_set_summary (context, "Summary:\n Rspamd administration utility version " RVERSION "\n Release id: " RID); g_option_context_add_main_entries (context, entries, NULL); g_option_context_set_ignore_unknown_options (context, TRUE); if (!g_option_context_parse (context, &argc, &argv, &error)) { rspamd_fprintf (stderr, "option parsing failed: %s\n", error->message); g_error_free (error); exit (1); } if (config_file) { /* Load config file, assuming that it has all information required */ struct ucl_parser *parser; parser = ucl_parser_new (0); rspamd_ucl_add_conf_variables (parser, ucl_vars); if (!ucl_parser_add_file (parser, config_file)) { msg_err ("ucl parser error: %s", ucl_parser_get_error (parser)); ucl_parser_free (parser); exit (EXIT_FAILURE); } obj = ucl_parser_get_object (parser); ucl_parser_free (parser); } else { /* We need to get all information from the command line */ ucl_object_t *classifier, *statfile_ham, *statfile_spam, *tmp, *redis; /* Check arguments sanity */ if (spam_db == NULL) { msg_err ("No spam-db specified"); exit (EXIT_FAILURE); } if (ham_db == NULL) { msg_err ("No ham-db specified"); exit (EXIT_FAILURE); } if (redis_host == NULL) { msg_err ("No redis-host specified"); exit (EXIT_FAILURE); } if (symbol_ham == NULL) { msg_err ("No symbol-ham specified"); exit (EXIT_FAILURE); } if (symbol_spam == NULL) { msg_err ("No symbol-spam specified"); exit (EXIT_FAILURE); } obj = ucl_object_typed_new (UCL_OBJECT); classifier = ucl_object_typed_new (UCL_OBJECT); ucl_object_insert_key (obj, classifier, "classifier", 0, false); /* Now we need to create "bayes" key in it */ tmp = ucl_object_typed_new (UCL_OBJECT); ucl_object_insert_key (classifier, tmp, "bayes", 0, false); classifier = tmp; ucl_object_insert_key (classifier, ucl_object_fromstring ("sqlite3"), "backend", 0, false); if (cache_db != NULL) { ucl_object_t *cache; cache = ucl_object_typed_new (UCL_OBJECT); ucl_object_insert_key (cache, ucl_object_fromstring ("sqlite3"), "type", 0, false); ucl_object_insert_key (cache, ucl_object_fromstring (cache_db), "file", 0, false); ucl_object_insert_key (classifier, cache, "cache", 0, false); } statfile_ham = ucl_object_typed_new (UCL_OBJECT); ucl_object_insert_key (statfile_ham, ucl_object_fromstring (symbol_ham), "symbol", 0, false); ucl_object_insert_key (statfile_ham, ucl_object_frombool (false), "spam", 0, false); ucl_object_insert_key (statfile_ham, ucl_object_fromstring (ham_db), "db", 0, false); statfile_spam = ucl_object_typed_new (UCL_OBJECT); ucl_object_insert_key (statfile_spam, ucl_object_fromstring (symbol_spam), "symbol", 0, false); ucl_object_insert_key (statfile_spam, ucl_object_frombool (true), "spam", 0, false); ucl_object_insert_key (statfile_spam, ucl_object_fromstring (spam_db), "db", 0, false); DL_APPEND (statfile_ham, statfile_spam); ucl_object_insert_key (classifier, statfile_ham, "statfile", 0, false); /* Deal with redis */ redis = ucl_object_typed_new (UCL_OBJECT); ucl_object_insert_key (obj, redis, "redis", 0, false); ucl_object_insert_key (redis, ucl_object_fromstring (redis_host), "servers", 0, false); if (redis_db) { ucl_object_insert_key (redis, ucl_object_fromstring (redis_db), "dbname", 0, false); } if (redis_password) { ucl_object_insert_key (redis, ucl_object_fromstring (redis_password), "password", 0, false); } } ucl_object_insert_key (obj, ucl_object_frombool (reset_previous), "reset_previous", 0, false); if (expire != 0) { ucl_object_insert_key (obj, ucl_object_fromdouble (expire), "expire", 0, false); } rspamadm_execute_lua_ucl_subr (argc, argv, obj, "stat_convert", TRUE); ucl_object_unref (obj); }
/* * fd_stat() * Do stat */ void fd_stat(struct msg *m, struct file *f) { char buf[MAXSTAT]; int size, ino; char type; struct floppy *fl = NULL; if (!(f->f_flags & ACC_READ)) { msg_err(m->m_sender, EPERM); return; } if (f->f_slot == ROOTDIR) { int x; size = 0; ino = 0; type = 'd'; /* * Work out the dir size */ for (x = 0; x < NFD; x++) { if (floppies[x].f_state != F_NXIO) { int y; struct floppy *fl = &floppies[x]; size++; for (y = 0; fl->f_posdens[y] != -1; y++) { size++; } } } } else { fl = unit(f->f_unit); size = fl->f_parms.f_size; ino = MKNODE(f->f_unit, f->f_slot) + UNITSTEP; type = 's'; } sprintf(buf, "size=%d\ntype=%c\nowner=0\ninode=%d\n" \ "fdc=%s\nirq=%d\ndma=%d\nbaseio=0x%0x\nretries=%d\n" \ "messages=%s\n", size, type, ino, fdc_names[fdc_type], fd_irq, fd_dma, fd_baseio, (f->f_slot == ROOTDIR ? fd_retries : fl->f_retries), fdm_opts[(f->f_slot == ROOTDIR ? fd_messages : fl->f_messages)]); strcat(buf, perm_print(&fd_prot)); if (f->f_slot != ROOTDIR) { /* * If we're a device we report on media changes and * the size of blocks we'll handle */ sprintf(&buf[strlen(buf)], "blksize=%d\nmediachg=%d\n", (size != FD_PUNDEF ? SECSZ(fl->f_parms.f_secsize) : 512), fl->f_mediachg); } if (f->f_slot == SPECIALNODE) { /* * If we're the special node we can deal with drive/media * parameters */ struct fdparms *fp; fp = (fl->f_specialdens == DISK_USERDEF) ? &fl->f_userp : &fl->f_parms; sprintf(&buf[strlen(buf)], "findparms=%s\n", fl->f_specialdens == DISK_USERDEF ? "userdef" : "auto"); if (fp->f_size == FD_PUNDEF) { sprintf(&buf[strlen(buf)], "parms=undefined\n"); } else { sprintf(&buf[strlen(buf)], "parms=%d:%d:%d:%d:%d:0x%02x:0x%02x:" \ "0x%02x:0x%02x:0x%02x:0x%02x\n", fp->f_size, fp->f_tracks, fp->f_heads, fp->f_sectors, fp->f_stretch, fp->f_gap, fp->f_fmtgap, fp->f_rate, fp->f_spec1, fp->f_spec2, fp->f_secsize); } } m->m_buf = buf; m->m_buflen = strlen(buf); m->m_nseg = 1; m->m_arg = m->m_arg1 = 0; msg_reply(m->m_sender, m); }
gboolean write_smtp_reply (struct smtp_session *session) { gchar logbuf[1024], *new_subject; const gchar *old_subject; struct smtp_metric_callback_data cd; GMimeStream *stream; gint old_fd, sublen; /* Check metrics */ cd.session = session; cd.action = METRIC_ACTION_NOACTION; cd.res = NULL; cd.log_buf = logbuf; cd.log_offset = rspamd_snprintf (logbuf, sizeof (logbuf), "id: <%s>, qid: <%s>, ", session->task->message_id, session->task->queue_id); cd.log_size = sizeof (logbuf); if (session->task->user) { cd.log_offset += rspamd_snprintf (logbuf + cd.log_offset, sizeof (logbuf) - cd.log_offset, "user: %s, ", session->task->user); } g_hash_table_foreach (session->task->results, smtp_metric_callback, &cd); msg_info ("%s", logbuf); if (cd.action <= METRIC_ACTION_REJECT) { if (!rspamd_dispatcher_write (session->dispatcher, session->ctx->reject_message, 0, FALSE, TRUE)) { return FALSE; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { return FALSE; } rspamd_session_destroy (session->s); return FALSE; } else if (cd.action <= METRIC_ACTION_ADD_HEADER || cd.action <= METRIC_ACTION_REWRITE_SUBJECT) { old_fd = session->temp_fd; if (!make_smtp_tempfile (session)) { session->error = SMTP_ERROR_FILE; session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { goto err; } rspamd_session_destroy (session->s); return FALSE; } if (cd.action <= METRIC_ACTION_REWRITE_SUBJECT) { /* XXX: add this action */ old_subject = g_mime_message_get_subject (session->task->message); if (old_subject != NULL) { sublen = strlen (old_subject) + sizeof (SPAM_SUBJECT); new_subject = rspamd_mempool_alloc (session->pool, sublen); rspamd_snprintf (new_subject, sublen, "%s%s", SPAM_SUBJECT, old_subject); } else { new_subject = SPAM_SUBJECT; } g_mime_message_set_subject (session->task->message, new_subject); } else if (cd.action <= METRIC_ACTION_ADD_HEADER) { #ifndef GMIME24 g_mime_message_add_header (session->task->message, "X-Spam", "true"); #else g_mime_object_append_header (GMIME_OBJECT ( session->task->message), "X-Spam", "true"); #endif } stream = g_mime_stream_fs_new (session->temp_fd); g_mime_stream_fs_set_owner (GMIME_STREAM_FS (stream), FALSE); close (old_fd); if (g_mime_object_write_to_stream (GMIME_OBJECT (session->task->message), stream) == -1) { msg_err ("cannot write MIME object to stream: %s", strerror (errno)); session->error = SMTP_ERROR_FILE; session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { goto err; } rspamd_session_destroy (session->s); return FALSE; } g_object_unref (stream); } /* XXX: Add other actions */ return smtp_send_upstream_message (session); err: session->error = SMTP_ERROR_FILE; session->state = SMTP_STATE_CRITICAL_ERROR; if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { return FALSE; } rspamd_session_destroy (session->s); return FALSE; }
static gint fuzzy_parse_rule (struct rspamd_config *cfg, const ucl_object_t *obj, gint cb_id) { const ucl_object_t *value, *cur; struct fuzzy_rule *rule; ucl_object_iter_t it = NULL; const char *k = NULL; if (obj->type != UCL_OBJECT) { msg_err ("invalid rule definition"); return -1; } rule = fuzzy_rule_new (fuzzy_module_ctx->default_symbol, fuzzy_module_ctx->fuzzy_pool); if ((value = ucl_object_find_key (obj, "mime_types")) != NULL) { it = NULL; while ((cur = ucl_iterate_object (value, &it, obj->type == UCL_ARRAY)) != NULL) { rule->mime_types = g_list_concat (rule->mime_types, parse_mime_types (ucl_obj_tostring (cur))); } } if (rule->mime_types != NULL) { rspamd_mempool_add_destructor (fuzzy_module_ctx->fuzzy_pool, (rspamd_mempool_destruct_t)g_list_free, rule->mime_types); } if ((value = ucl_object_find_key (obj, "max_score")) != NULL) { rule->max_score = ucl_obj_todouble (value); } if ((value = ucl_object_find_key (obj, "symbol")) != NULL) { rule->symbol = ucl_obj_tostring (value); } if ((value = ucl_object_find_key (obj, "read_only")) != NULL) { rule->read_only = ucl_obj_toboolean (value); } if ((value = ucl_object_find_key (obj, "skip_unknown")) != NULL) { rule->skip_unknown = ucl_obj_toboolean (value); } if ((value = ucl_object_find_key (obj, "servers")) != NULL) { rule->servers = rspamd_upstreams_create (); rspamd_mempool_add_destructor (fuzzy_module_ctx->fuzzy_pool, (rspamd_mempool_destruct_t)rspamd_upstreams_destroy, rule->servers); rspamd_upstreams_from_ucl (rule->servers, value, DEFAULT_PORT, NULL); } if ((value = ucl_object_find_key (obj, "fuzzy_map")) != NULL) { it = NULL; while ((cur = ucl_iterate_object (value, &it, true)) != NULL) { parse_flags (rule, cfg, cur, cb_id); } } if ((value = ucl_object_find_key (obj, "fuzzy_key")) != NULL) { /* Create key from user's input */ k = ucl_object_tostring (value); } /* Setup keys */ if (k == NULL) { /* Use some default key for all ops */ k = "rspamd"; } rule->hash_key = g_string_sized_new (BLAKE2B_KEYBYTES); blake2 (rule->hash_key->str, k, NULL, BLAKE2B_KEYBYTES, strlen (k), 0); rule->hash_key->len = BLAKE2B_KEYBYTES; if ((value = ucl_object_find_key (obj, "fuzzy_shingles_key")) != NULL) { k = ucl_object_tostring (value); } if (k == NULL) { k = "rspamd"; } rule->shingles_key = g_string_sized_new (16); blake2 (rule->shingles_key->str, k, NULL, 16, strlen (k), 0); rule->shingles_key->len = 16; if (rspamd_upstreams_count (rule->servers) == 0) { msg_err ("no servers defined for fuzzy rule with symbol: %s", rule->symbol); return -1; } else { fuzzy_module_ctx->fuzzy_rules = g_list_prepend ( fuzzy_module_ctx->fuzzy_rules, rule); if (rule->symbol != fuzzy_module_ctx->default_symbol) { rspamd_symbols_cache_add_symbol_virtual (cfg->cache, rule->symbol, 1.0, cb_id); } } rspamd_mempool_add_destructor (fuzzy_module_ctx->fuzzy_pool, fuzzy_free_rule, rule); return 0; }
gboolean rspamd_lua_check_condition (struct rspamd_config *cfg, const gchar *condition) { lua_State *L = cfg->lua_state; gchar *hostbuf, *condbuf; gsize hostlen; gboolean res; #ifdef HAVE_SYS_UTSNAME_H struct utsname uts; #endif /* Set some globals for condition */ /* XXX: think what other variables can be useful */ hostlen = sysconf (_SC_HOST_NAME_MAX) + 1; hostbuf = alloca (hostlen); gethostname (hostbuf, hostlen); hostbuf[hostlen - 1] = '\0'; /* Hostname */ lua_pushstring (L, hostbuf); lua_setglobal (L, "hostname"); /* Config file name */ lua_pushstring (L, cfg->cfg_name); lua_setglobal (L, "cfg_name"); /* Check for uname */ #ifdef HAVE_SYS_UTSNAME_H uname (&uts); lua_pushstring (L, uts.sysname); lua_setglobal (L, "osname"); lua_pushstring (L, uts.release); lua_setglobal (L, "osrelease"); #else lua_pushstring (L, "unknown"); lua_setglobal (L, "osname"); lua_pushstring (L, ""); lua_setglobal (L, "osrelease"); #endif #ifdef HAVE_OPENSSL lua_pushboolean (L, TRUE); #else lua_pushboolean (L, FALSE); #endif lua_setglobal (L, "rspamd_supports_rsa"); /* Rspamd paths */ lua_newtable (L); rspamd_lua_table_set (L, "confdir", RSPAMD_CONFDIR); rspamd_lua_table_set (L, "rundir", RSPAMD_RUNDIR); rspamd_lua_table_set (L, "dbdir", RSPAMD_DBDIR); rspamd_lua_table_set (L, "logdir", RSPAMD_LOGDIR); rspamd_lua_table_set (L, "pluginsdir", RSPAMD_PLUGINSDIR); rspamd_lua_table_set (L, "prefix", RSPAMD_PREFIX); lua_setglobal (L, "rspamd_paths"); /* Make fake string */ hostlen = sizeof (FAKE_RES_VAR "=") + strlen (condition); condbuf = g_malloc (hostlen); rspamd_strlcpy (condbuf, FAKE_RES_VAR "=", sizeof (FAKE_RES_VAR "=")); g_strlcat (condbuf, condition, hostlen); /* Evaluate condition */ if (luaL_dostring (L, condbuf) != 0) { msg_err ("eval of '%s' failed: '%s'", condition, lua_tostring (L, -1)); g_free (condbuf); return FALSE; } /* Get global variable res to get result */ lua_getglobal (L, FAKE_RES_VAR); if (!lua_isboolean (L, -1)) { msg_err ("bad string evaluated: %s, type: %s", condbuf, lua_typename (L, lua_type (L, -1))); g_free (condbuf); return FALSE; } res = lua_toboolean (L, -1); g_free (condbuf); return res; }
struct rspamd_worker * rspamd_fork_worker (struct rspamd_main *rspamd_main, struct rspamd_worker_conf *cf, guint index) { struct rspamd_worker *cur; /* Starting worker process */ cur = (struct rspamd_worker *) g_malloc0 (sizeof (struct rspamd_worker)); if (!rspamd_socketpair (cur->control_pipe)) { msg_err ("socketpair failure: %s", strerror (errno)); exit (-errno); } cur->srv = rspamd_main; cur->type = cf->type; cur->cf = g_malloc (sizeof (struct rspamd_worker_conf)); memcpy (cur->cf, cf, sizeof (struct rspamd_worker_conf)); cur->index = index; cur->ctx = cf->ctx; cur->pid = fork (); switch (cur->pid) { case 0: /* Update pid for logging */ rspamd_log_update_pid (cf->type, rspamd_main->logger); /* Lock statfile pool if possible XXX */ /* Init PRNG after fork */ ottery_init (NULL); g_random_set_seed (ottery_rand_uint32 ()); /* Drop privilleges */ rspamd_worker_drop_priv (rspamd_main); /* Set limits */ rspamd_worker_set_limits (rspamd_main, cf); setproctitle ("%s process", cf->worker->name); rspamd_pidfile_close (rspamd_main->pfh); /* Do silent log reopen to avoid collisions */ rspamd_log_close (rspamd_main->logger); rspamd_log_open (rspamd_main->logger); cur->start_time = rspamd_get_calendar_ticks (); #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION <= 30)) # if (GLIB_MINOR_VERSION > 20) /* Ugly hack for old glib */ if (!g_thread_get_initialized ()) { g_thread_init (NULL); } # else g_thread_init (NULL); # endif #endif msg_info_main ("starting %s process %P", cf->worker->name, getpid ()); /* Close parent part of socketpair */ close (cur->control_pipe[0]); rspamd_socket_nonblocking (cur->control_pipe[1]); /* Execute worker */ cf->worker->worker_start_func (cur); break; case -1: msg_err_main ("cannot fork main process. %s", strerror (errno)); rspamd_pidfile_remove (rspamd_main->pfh); exit (-errno); break; default: /* Close worker part of socketpair */ close (cur->control_pipe[1]); rspamd_socket_nonblocking (cur->control_pipe[0]); /* Insert worker into worker's table, pid is index */ g_hash_table_insert (rspamd_main->workers, GSIZE_TO_POINTER ( cur->pid), cur); break; } return cur; }
static struct _pool_chain * rspamd_mempool_chain_new (gsize size, enum rspamd_mempool_chain_type pool_type) { struct _pool_chain *chain; gpointer map; g_return_val_if_fail (size > 0, NULL); if (pool_type == RSPAMD_MEMPOOL_SHARED) { #if defined(HAVE_MMAP_ANON) map = mmap (NULL, size + sizeof (struct _pool_chain), PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); if (map == MAP_FAILED) { msg_err ("cannot allocate %z bytes of shared memory, aborting", size + sizeof (struct _pool_chain)); abort (); } chain = map; chain->begin = ((guint8 *) chain) + sizeof (struct _pool_chain); #elif defined(HAVE_MMAP_ZERO) gint fd; fd = open ("/dev/zero", O_RDWR); if (fd == -1) { return NULL; } map = mmap (NULL, size + sizeof (struct _pool_chain), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { msg_err ("cannot allocate %z bytes, aborting", size + sizeof (struct _pool_chain)); abort (); } chain = map; chain->begin = ((guint8 *) chain) + sizeof (struct _pool_chain); #else #error No mmap methods are defined #endif g_atomic_int_inc (&mem_pool_stat->shared_chunks_allocated); g_atomic_int_add (&mem_pool_stat->bytes_allocated, size); } else { map = g_slice_alloc (sizeof (struct _pool_chain) + size); chain = map; chain->begin = ((guint8 *) chain) + sizeof (struct _pool_chain); g_atomic_int_add (&mem_pool_stat->bytes_allocated, size); g_atomic_int_inc (&mem_pool_stat->chunks_allocated); } chain->pos = align_ptr (chain->begin, MEM_ALIGNMENT); chain->len = size; chain->lock = NULL; return chain; }
gint rspamd_symbols_cache_add_symbol (struct symbols_cache *cache, const gchar *name, double weight, gint priority, symbol_func_t func, gpointer user_data, enum rspamd_symbol_type type, gint parent) { struct cache_item *item = NULL; g_assert (cache != NULL); if (name == NULL && type != SYMBOL_TYPE_CALLBACK) { msg_warn ("no name for non-callback symbol!"); } else if (type == SYMBOL_TYPE_VIRTUAL && parent == -1) { msg_warn ("no parent symbol is associated with virtual symbol %s", name); } if (name != NULL) { if (g_hash_table_lookup (cache->items_by_symbol, name) != NULL) { msg_err ("skip duplicate symbol registration for %s", name); return -1; } } item = rspamd_mempool_alloc0_shared (cache->static_pool, sizeof (struct cache_item)); /* * We do not share cd to skip locking, instead we'll just calculate it on * save or accumulate */ item->cd = rspamd_mempool_alloc0 (cache->static_pool, sizeof (struct counter_data)); if (name != NULL) { item->symbol = rspamd_mempool_strdup (cache->static_pool, name); } item->func = func; item->user_data = user_data; item->priority = priority; item->type = type; item->weight = weight; if (item->weight < 0 && item->priority == 0) { /* Make priority for negative weighted symbols */ item->priority = 1; } item->id = cache->used_items; item->parent = parent; cache->used_items ++; msg_debug ("used items: %d, added symbol: %s", cache->used_items, name); rspamd_set_counter (item, 0); g_ptr_array_add (cache->items_by_id, item); g_ptr_array_add (cache->items_by_order, item); item->deps = g_ptr_array_new (); item->rdeps = g_ptr_array_new (); rspamd_mempool_add_destructor (cache->static_pool, rspamd_ptr_array_free_hard, item->deps); rspamd_mempool_add_destructor (cache->static_pool, rspamd_ptr_array_free_hard, item->rdeps); if (name != NULL) { g_hash_table_insert (cache->items_by_symbol, item->symbol, item); } return item->id; }
/* Called when we have connected to the redis server and got keys to check */ static void rspamd_redis_stat_keys (redisAsyncContext *c, gpointer r, gpointer priv) { struct rspamd_redis_stat_cbdata *cbdata = priv; redisReply *reply = r, *elt; gchar **pk, *k; guint i, processed = 0; if (cbdata->wanna_die) { return; } cbdata->inflight --; if (c->err == 0 && r != NULL) { if (reply->type == REDIS_REPLY_ARRAY) { g_ptr_array_set_size (cbdata->cur_keys, reply->elements); for (i = 0; i < reply->elements; i ++) { elt = reply->element[i]; if (elt->type == REDIS_REPLY_STRING) { pk = (gchar **)&g_ptr_array_index (cbdata->cur_keys, i); *pk = g_malloc (elt->len + 1); rspamd_strlcpy (*pk, elt->str, elt->len + 1); processed ++; } } if (processed) { for (i = 0; i < cbdata->cur_keys->len; i ++) { k = (gchar *)g_ptr_array_index (cbdata->cur_keys, i); if (k) { redisAsyncCommand (cbdata->redis, rspamd_redis_stat_key, cbdata, "HLEN %s", k); redisAsyncCommand (cbdata->redis, rspamd_redis_stat_learns, cbdata, "HGET %s learns", k); cbdata->inflight += 2; } } } } /* Set up the required keys */ ucl_object_insert_key (cbdata->cur, ucl_object_typed_new (UCL_INT), "revision", 0, false); ucl_object_insert_key (cbdata->cur, ucl_object_typed_new (UCL_INT), "used", 0, false); ucl_object_insert_key (cbdata->cur, ucl_object_typed_new (UCL_INT), "total", 0, false); ucl_object_insert_key (cbdata->cur, ucl_object_typed_new (UCL_INT), "size", 0, false); ucl_object_insert_key (cbdata->cur, ucl_object_fromstring (cbdata->elt->ctx->stcf->symbol), "symbol", 0, false); ucl_object_insert_key (cbdata->cur, ucl_object_fromstring ("redis"), "type", 0, false); ucl_object_insert_key (cbdata->cur, ucl_object_fromint (0), "languages", 0, false); ucl_object_insert_key (cbdata->cur, ucl_object_fromint (processed), "users", 0, false); rspamd_upstream_ok (cbdata->selected); } else { msg_err ("cannot get keys to gather stat"); rspamd_upstream_fail (cbdata->selected); rspamd_redis_async_cbdata_cleanup (cbdata); } if (cbdata->inflight == 0) { rspamd_redis_async_cbdata_cleanup (cbdata); } }
static gint lua_worker_spawn_process (lua_State *L) { struct rspamd_worker *w = lua_check_worker (L, 1); struct rspamd_lua_process_cbdata *cbdata; struct rspamd_abstract_worker_ctx *actx; struct rspamd_srv_command srv_cmd; const gchar *cmdline = NULL, *input = NULL; gsize inputlen = 0; pid_t pid; GError *err = NULL; gint func_cbref, cb_cbref; if (!rspamd_lua_parse_table_arguments (L, 2, &err, "func=F;exec=S;stdin=V;*on_complete=F", &func_cbref, &cmdline, &inputlen, &input, &cb_cbref)) { msg_err ("cannot get parameters list: %e", err); if (err) { g_error_free (err); } return 0; } cbdata = g_malloc0 (sizeof (*cbdata)); cbdata->cb_cbref = cb_cbref; cbdata->func_cbref = func_cbref; if (input) { cbdata->out_buf = g_string_new_len (input, inputlen); cbdata->out_pos = 0; } if (rspamd_socketpair (cbdata->sp, TRUE) == -1) { msg_err ("cannot spawn socketpair: %s", strerror (errno)); luaL_unref (L, LUA_REGISTRYINDEX, cbdata->func_cbref); luaL_unref (L, LUA_REGISTRYINDEX, cbdata->cb_cbref); g_free (cbdata); return 0; } actx = w->ctx; cbdata->wrk = w; cbdata->L = L; cbdata->ev_base = actx->ev_base; cbdata->sz = (guint64)-1; pid = fork (); if (pid == -1) { msg_err ("cannot spawn process: %s", strerror (errno)); close (cbdata->sp[0]); close (cbdata->sp[1]); luaL_unref (L, LUA_REGISTRYINDEX, cbdata->func_cbref); luaL_unref (L, LUA_REGISTRYINDEX, cbdata->cb_cbref); g_free (cbdata); return 0; } else if (pid == 0) { /* Child */ gint rc; gchar inbuf[4]; rspamd_log_update_pid (w->cf->type, w->srv->logger); rc = ottery_init (w->srv->cfg->libs_ctx->ottery_cfg); if (rc != OTTERY_ERR_NONE) { msg_err ("cannot initialize PRNG: %d", rc); abort (); } rspamd_random_seed_fast (); #ifdef HAVE_EVUTIL_RNG_INIT evutil_secure_rng_init (); #endif close (cbdata->sp[0]); /* Here we assume that we can block on writing results */ rspamd_socket_blocking (cbdata->sp[1]); event_reinit (cbdata->ev_base); g_hash_table_remove_all (w->signal_events); rspamd_worker_unblock_signals (); rspamd_lua_execute_lua_subprocess (L, cbdata); /* Wait for parent to reply and exit */ rc = read (cbdata->sp[1], inbuf, sizeof (inbuf)); if (memcmp (inbuf, "\0\0\0\0", 4) == 0) { exit (EXIT_SUCCESS); } else { msg_err ("got invalid reply from parent"); exit (EXIT_FAILURE); } } cbdata->cpid = pid; cbdata->io_buf = g_string_sized_new (8); /* Notify main */ memset (&srv_cmd, 0, sizeof (srv_cmd)); srv_cmd.type = RSPAMD_SRV_ON_FORK; srv_cmd.cmd.on_fork.state = child_create; srv_cmd.cmd.on_fork.cpid = pid; srv_cmd.cmd.on_fork.ppid = getpid (); rspamd_srv_send_command (w, cbdata->ev_base, &srv_cmd, -1, NULL, NULL); close (cbdata->sp[1]); rspamd_socket_nonblocking (cbdata->sp[0]); /* Parent */ rspamd_worker_set_signal_handler (SIGCHLD, w, cbdata->ev_base, rspamd_lua_cld_handler, cbdata); /* Add result pipe waiting */ event_set (&cbdata->ev, cbdata->sp[0], EV_READ | EV_PERSIST, rspamd_lua_subprocess_io, cbdata); event_base_set (cbdata->ev_base, &cbdata->ev); /* TODO: maybe add timeout? */ event_add (&cbdata->ev, NULL); return 0; }
static int check_specific_limit(struct mlfi_priv *priv, struct config_file *cfg, enum keytype type, bucket_t *bucket, double tm, const char *rcpt, int is_update) { struct memcached_server *selected; struct ratelimit_bucket_s *b; char key[MAXKEYLEN]; size_t klen, dlen; if (bucket->burst == 0 || bucket->rate == 0) { return 1; } klen = make_key (key, sizeof(key), type, priv, rcpt); if (klen == 0) { msg_err("<%s>; check_specific_limit: got error bad too long key", priv->mlfi_id); return -1; } dlen = sizeof (*b); if (!rmilter_query_cache (cfg, RMILTER_QUERY_RATELIMIT, key, klen, (unsigned char **) &b, &dlen, priv)) { b = calloc (1, sizeof (*b)); dlen = sizeof (*b); if (b == NULL) { msg_err("<%s>; check_specific_limit: calloc failed: %s", priv->mlfi_id, strerror (errno)); return -1; } } msg_debug("<%s>; check_specific_limit: got limit for key: '%s', " "count: %.1f, time: %.1f", priv->mlfi_id, key, b->count, b->tm); /* Leak from bucket at specified rate */ if (b->count > 0) { b->count -= (tm - b->tm) * bucket->rate; } b->count += is_update; b->tm = tm; if (b->count < 0) { b->count = 0; } if (is_update && b->count == 0) { /* Delete key if bucket is empty */ rmilter_delete_cache (cfg, RMILTER_QUERY_RATELIMIT, key, klen, priv); } else { /* Update rate limit */ rmilter_set_cache (cfg, RMILTER_QUERY_RATELIMIT, key, klen, (unsigned char *) b, dlen, EXPIRE_TIME, priv); } if (b->count > bucket->burst && !is_update) { /* Rate limit exceeded */ msg_info( "<%s>; rate_check: ratelimit exceeded for key: %s, count: %.2f, burst: %u", priv->mlfi_id, key, b->count, bucket->burst); free (b); return 0; } free (b); /* Rate limit not exceeded */ return 1; }
/* PCRE 2 version */ gboolean rspamd_regexp_search (rspamd_regexp_t *re, const gchar *text, gsize len, const gchar **start, const gchar **end, gboolean raw, GArray *captures) { pcre2_match_data *match_data; pcre2_match_context *mcontext; PCRE_T *r; const gchar *mt; gsize remain = 0, *ovec; gint rc, match_flags, novec, i; gboolean ret = FALSE; g_assert (re != NULL); g_assert (text != NULL); if (len == 0) { len = strlen (text); } if (end != NULL && *end != NULL) { /* Incremental search */ mt = (*end); if ((gint)len > (mt - text)) { remain = len - (mt - text); } } else { mt = text; remain = len; } if (remain == 0) { return FALSE; } match_flags = 0; if (raw || re->re == re->raw_re) { r = re->raw_re; mcontext = re->raw_mcontext; } else { r = re->re; mcontext = re->mcontext; } match_data = pcre2_match_data_create (re->ncaptures + 1, NULL); #ifdef HAVE_PCRE_JIT if (!(re->flags & RSPAMD_REGEXP_FLAG_DISABLE_JIT) && can_jit) { if (re->re != re->raw_re && !g_utf8_validate (mt, remain, NULL)) { msg_err ("bad utf8 input for JIT re"); return FALSE; } rc = pcre2_jit_match (r, mt, remain, 0, match_flags, match_data, mcontext); } else { rc = pcre2_match (r, mt, remain, 0, match_flags, match_data, mcontext); } #else rc = pcre2_match (r, mt, remain, 0, match_flags, match_data, mcontext); #endif if (rc >= 0) { novec = pcre2_get_ovector_count (match_data); ovec = pcre2_get_ovector_pointer (match_data); if (start) { *start = mt + ovec[0]; } if (end) { *end = mt + ovec[1]; } if (captures != NULL && novec > 1) { struct rspamd_re_capture *elt; g_assert (g_array_get_element_size (captures) == sizeof (struct rspamd_re_capture)); g_array_set_size (captures, novec); for (i = 0; i < novec; i ++) { elt = &g_array_index (captures, struct rspamd_re_capture, i); elt->p = mt + ovec[i * 2]; elt->len = (mt + ovec[i * 2 + 1]) - elt->p; } } ret = TRUE; if (re->flags & RSPAMD_REGEXP_FLAG_FULL_MATCH) { /* We also ensure that the match is full */ if (ovec[0] != 0 || (guint)ovec[1] < len) { ret = FALSE; } } }