You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cfg_utils.c 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975
  1. /*
  2. * Copyright (c) 2009-2012, Vsevolod Stakhov
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
  17. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  18. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  19. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  20. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  22. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. */
  24. #include "config.h"
  25. #include "cfg_file.h"
  26. #include "main.h"
  27. #include "filter.h"
  28. #include "settings.h"
  29. #include "classifiers/classifiers.h"
  30. #include "cfg_xml.h"
  31. #include "lua/lua_common.h"
  32. #include "kvstorage_config.h"
  33. #include "map.h"
  34. #include "dynamic_cfg.h"
  35. #define DEFAULT_SCORE 10.0
  36. #define DEFAULT_RLIMIT_NOFILE 2048
  37. #define DEFAULT_RLIMIT_MAXCORE 0
  38. #define DEFAULT_MAP_TIMEOUT 10
  39. gboolean
  40. parse_host_port_priority (memory_pool_t *pool, const gchar *str, gchar **addr, guint16 *port, guint *priority)
  41. {
  42. gchar **tokens, *err_str, *cur_tok;
  43. struct addrinfo hints, *res;
  44. guint port_parsed, priority_parsed, saved_errno = errno;
  45. gint r;
  46. union {
  47. struct sockaddr_in v4;
  48. struct sockaddr_in6 v6;
  49. } addr_holder;
  50. tokens = g_strsplit_set (str, ":", 0);
  51. if (!tokens || !tokens[0]) {
  52. return FALSE;
  53. }
  54. /* Now try to parse host and write address to ina */
  55. memset (&hints, 0, sizeof (hints));
  56. hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
  57. hints.ai_socktype = SOCK_STREAM; /* Type of the socket */
  58. hints.ai_flags = 0;
  59. hints.ai_protocol = 0; /* Any protocol */
  60. hints.ai_canonname = NULL;
  61. hints.ai_addr = NULL;
  62. hints.ai_next = NULL;
  63. if (strcmp (tokens[0], "*") == 0) {
  64. /* XXX: actually we still cannot listen on multiply protocols */
  65. if (pool != NULL) {
  66. *addr = memory_pool_alloc (pool, INET_ADDRSTRLEN + 1);
  67. }
  68. rspamd_strlcpy (*addr, "0.0.0.0", INET_ADDRSTRLEN + 1);
  69. goto port_parse;
  70. }
  71. else {
  72. cur_tok = tokens[0];
  73. }
  74. if ((r = getaddrinfo (cur_tok, NULL, &hints, &res)) == 0) {
  75. memcpy (&addr_holder, res->ai_addr, MIN (sizeof (addr_holder), res->ai_addrlen));
  76. if (res->ai_family == AF_INET) {
  77. if (pool != NULL) {
  78. *addr = memory_pool_alloc (pool, INET_ADDRSTRLEN + 1);
  79. }
  80. inet_ntop (res->ai_family, &addr_holder.v4.sin_addr, *addr, INET_ADDRSTRLEN + 1);
  81. }
  82. else {
  83. if (pool != NULL) {
  84. *addr = memory_pool_alloc (pool, INET6_ADDRSTRLEN + 1);
  85. }
  86. inet_ntop (res->ai_family, &addr_holder.v6.sin6_addr, *addr, INET6_ADDRSTRLEN + 1);
  87. }
  88. freeaddrinfo (res);
  89. }
  90. else {
  91. msg_err ("address resolution for %s failed: %s", tokens[0], gai_strerror (r));
  92. goto err;
  93. }
  94. port_parse:
  95. if (tokens[1] != NULL) {
  96. /* Port part */
  97. if (port != NULL) {
  98. errno = 0;
  99. port_parsed = strtoul (tokens[1], &err_str, 10);
  100. if (*err_str != '\0' || errno != 0) {
  101. msg_warn ("cannot parse port: %s, at symbol %c, error: %s", tokens[1], *err_str, strerror (errno));
  102. goto err;
  103. }
  104. if (port_parsed > G_MAXUINT16) {
  105. errno = ERANGE;
  106. msg_warn ("cannot parse port: %s, error: %s", tokens[1], *err_str, strerror (errno));
  107. goto err;
  108. }
  109. *port = port_parsed;
  110. }
  111. if (priority != NULL) {
  112. if (port != NULL) {
  113. cur_tok = tokens[2];
  114. }
  115. else {
  116. cur_tok = tokens[1];
  117. }
  118. if (cur_tok != NULL) {
  119. /* Priority part */
  120. errno = 0;
  121. priority_parsed = strtoul (cur_tok, &err_str, 10);
  122. if (*err_str != '\0' || errno != 0) {
  123. msg_warn ("cannot parse priority: %s, at symbol %c, error: %s", tokens[1], *err_str, strerror (errno));
  124. goto err;
  125. }
  126. *priority = priority_parsed;
  127. }
  128. }
  129. }
  130. /* Restore errno */
  131. errno = saved_errno;
  132. g_strfreev (tokens);
  133. return TRUE;
  134. err:
  135. errno = saved_errno;
  136. g_strfreev (tokens);
  137. return FALSE;
  138. }
  139. gboolean
  140. parse_host_port (memory_pool_t *pool, const gchar *str, gchar **addr, guint16 *port)
  141. {
  142. return parse_host_port_priority (pool, str, addr, port, NULL);
  143. }
  144. gboolean
  145. parse_host_priority (memory_pool_t *pool, const gchar *str, gchar **addr, guint *priority)
  146. {
  147. return parse_host_port_priority (pool, str, addr, NULL, priority);
  148. }
  149. gboolean
  150. parse_bind_line (struct config_file *cfg, struct worker_conf *cf, gchar *str)
  151. {
  152. struct rspamd_worker_bind_conf *cnf;
  153. if (str == NULL)
  154. return 0;
  155. cnf = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct rspamd_worker_bind_conf));
  156. cnf->bind_port = DEFAULT_BIND_PORT;
  157. if (str[0] == '/' || str[0] == '.') {
  158. #ifdef HAVE_DIRNAME
  159. /* Try to check path of bind credit */
  160. struct stat st;
  161. gint fd;
  162. gchar *copy = memory_pool_strdup (cfg->cfg_pool, str);
  163. if (stat (copy, &st) == -1) {
  164. if (errno == ENOENT) {
  165. if ((fd = open (str, O_RDWR | O_TRUNC | O_CREAT, S_IWUSR | S_IRUSR)) == -1) {
  166. msg_err ("cannot open path %s for making socket, %s", str, strerror (errno));
  167. return FALSE;
  168. }
  169. else {
  170. close (fd);
  171. unlink (str);
  172. }
  173. }
  174. else {
  175. msg_err ("cannot stat path %s for making socket, %s", str, strerror (errno));
  176. return 0;
  177. }
  178. }
  179. else {
  180. if (unlink (str) == -1) {
  181. msg_err ("cannot remove path %s for making socket, %s", str, strerror (errno));
  182. return 0;
  183. }
  184. }
  185. #endif
  186. cnf->bind_host = memory_pool_strdup (cfg->cfg_pool, str);
  187. cnf->is_unix = TRUE;
  188. LL_PREPEND (cf->bind_conf, cnf);
  189. return TRUE;
  190. }
  191. else {
  192. cnf->bind_host = memory_pool_strdup (cfg->cfg_pool, str);
  193. if (parse_host_port (cfg->cfg_pool, str, &cnf->bind_host, &cnf->bind_port)) {
  194. LL_PREPEND (cf->bind_conf, cnf);
  195. return TRUE;
  196. }
  197. }
  198. return FALSE;
  199. }
  200. void
  201. init_defaults (struct config_file *cfg)
  202. {
  203. cfg->memcached_error_time = DEFAULT_UPSTREAM_ERROR_TIME;
  204. cfg->memcached_dead_time = DEFAULT_UPSTREAM_DEAD_TIME;
  205. cfg->memcached_maxerrors = DEFAULT_UPSTREAM_MAXERRORS;
  206. cfg->memcached_protocol = TCP_TEXT;
  207. cfg->dns_timeout = 1000;
  208. cfg->dns_retransmits = 5;
  209. /* After 20 errors do throttling for 10 seconds */
  210. cfg->dns_throttling_errors = 20;
  211. cfg->dns_throttling_time = 10000;
  212. cfg->statfile_sync_interval = 60000;
  213. cfg->statfile_sync_timeout = 20000;
  214. /* 20 Kb */
  215. cfg->max_diff = 20480;
  216. cfg->max_statfile_size = DEFAULT_STATFILE_SIZE;
  217. cfg->metrics = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  218. cfg->c_modules = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  219. cfg->composite_symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  220. cfg->classifiers_symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  221. cfg->cfg_params = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  222. cfg->metrics_symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  223. cfg->map_timeout = DEFAULT_MAP_TIMEOUT;
  224. cfg->log_level = G_LOG_LEVEL_WARNING;
  225. cfg->log_extended = TRUE;
  226. init_settings (cfg);
  227. }
  228. void
  229. free_config (struct config_file *cfg)
  230. {
  231. GList *cur;
  232. struct symbols_group *gr;
  233. remove_all_maps (cfg);
  234. ucl_obj_unref (cfg->rcl_obj);
  235. g_hash_table_remove_all (cfg->metrics);
  236. g_hash_table_unref (cfg->metrics);
  237. g_hash_table_remove_all (cfg->c_modules);
  238. g_hash_table_unref (cfg->c_modules);
  239. g_hash_table_remove_all (cfg->composite_symbols);
  240. g_hash_table_unref (cfg->composite_symbols);
  241. g_hash_table_remove_all (cfg->cfg_params);
  242. g_hash_table_unref (cfg->cfg_params);
  243. g_hash_table_destroy (cfg->metrics_symbols);
  244. g_hash_table_destroy (cfg->classifiers_symbols);
  245. /* Free symbols groups */
  246. cur = cfg->symbols_groups;
  247. while (cur) {
  248. gr = cur->data;
  249. if (gr->symbols) {
  250. g_list_free (gr->symbols);
  251. }
  252. cur = g_list_next (cur);
  253. }
  254. if (cfg->symbols_groups) {
  255. g_list_free (cfg->symbols_groups);
  256. }
  257. if (cfg->checksum) {
  258. g_free (cfg->checksum);
  259. }
  260. g_list_free (cfg->classifiers);
  261. g_list_free (cfg->metrics_list);
  262. memory_pool_delete (cfg->cfg_pool);
  263. }
  264. ucl_object_t *
  265. get_module_opt (struct config_file *cfg, const gchar *module_name, const gchar *opt_name)
  266. {
  267. ucl_object_t *res = NULL, *sec;
  268. sec = ucl_obj_get_key (cfg->rcl_obj, module_name);
  269. if (sec != NULL) {
  270. res = ucl_obj_get_key (sec, opt_name);
  271. }
  272. return res;
  273. }
  274. guint64
  275. parse_limit (const gchar *limit, guint len)
  276. {
  277. guint64 result = 0;
  278. const gchar *err_str;
  279. if (!limit || *limit == '\0' || len == 0) {
  280. return 0;
  281. }
  282. errno = 0;
  283. result = strtoull (limit, (gchar **)&err_str, 10);
  284. if (*err_str != '\0') {
  285. /* Megabytes */
  286. if (*err_str == 'm' || *err_str == 'M') {
  287. result *= 1048576L;
  288. }
  289. /* Kilobytes */
  290. else if (*err_str == 'k' || *err_str == 'K') {
  291. result *= 1024;
  292. }
  293. /* Gigabytes */
  294. else if (*err_str == 'g' || *err_str == 'G') {
  295. result *= 1073741824L;
  296. }
  297. else if (len > 0 && err_str - limit != (gint)len) {
  298. msg_warn ("invalid limit value '%s' at position '%s'", limit, err_str);
  299. result = 0;
  300. }
  301. }
  302. return result;
  303. }
  304. gchar
  305. parse_flag (const gchar *str)
  306. {
  307. guint len;
  308. gchar c;
  309. if (!str || !*str) {
  310. return -1;
  311. }
  312. len = strlen (str);
  313. switch (len) {
  314. case 1:
  315. c = g_ascii_tolower (*str);
  316. if (c == 'y' || c == '1') {
  317. return 1;
  318. }
  319. else if (c == 'n' || c == '0') {
  320. return 0;
  321. }
  322. break;
  323. case 2:
  324. if (g_ascii_strncasecmp (str, "no", len) == 0) {
  325. return 0;
  326. }
  327. else if (g_ascii_strncasecmp (str, "on", len) == 0) {
  328. return 1;
  329. }
  330. break;
  331. case 3:
  332. if (g_ascii_strncasecmp (str, "yes", len) == 0) {
  333. return 1;
  334. }
  335. else if (g_ascii_strncasecmp (str, "off", len) == 0) {
  336. return 0;
  337. }
  338. break;
  339. case 4:
  340. if (g_ascii_strncasecmp (str, "true", len) == 0) {
  341. return 1;
  342. }
  343. break;
  344. case 5:
  345. if (g_ascii_strncasecmp (str, "false", len) == 0) {
  346. return 0;
  347. }
  348. break;
  349. }
  350. return -1;
  351. }
  352. gboolean
  353. get_config_checksum (struct config_file *cfg)
  354. {
  355. gint fd;
  356. void *map;
  357. struct stat st;
  358. /* Compute checksum for config file that should be used by xml dumper */
  359. if ((fd = open (cfg->cfg_name, O_RDONLY)) == -1) {
  360. msg_err ("config file %s is no longer available, cannot calculate checksum");
  361. return FALSE;
  362. }
  363. if (stat (cfg->cfg_name, &st) == -1) {
  364. msg_err ("cannot stat %s: %s", cfg->cfg_name, strerror (errno));
  365. return FALSE;
  366. }
  367. /* Now mmap this file to simplify reading process */
  368. if ((map = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
  369. msg_err ("cannot mmap %s: %s", cfg->cfg_name, strerror (errno));
  370. close (fd);
  371. return FALSE;
  372. }
  373. close (fd);
  374. /* Get checksum for a file */
  375. cfg->checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, map, st.st_size);
  376. munmap (map, st.st_size);
  377. return TRUE;
  378. }
  379. /*
  380. * Perform post load actions
  381. */
  382. void
  383. post_load_config (struct config_file *cfg)
  384. {
  385. #ifdef HAVE_CLOCK_GETTIME
  386. struct timespec ts;
  387. #endif
  388. struct metric *def_metric;
  389. #ifdef HAVE_CLOCK_GETTIME
  390. #ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID
  391. clock_getres (CLOCK_PROCESS_CPUTIME_ID, &ts);
  392. # elif defined(HAVE_CLOCK_VIRTUAL)
  393. clock_getres (CLOCK_VIRTUAL, &ts);
  394. # else
  395. clock_getres (CLOCK_REALTIME, &ts);
  396. # endif
  397. cfg->clock_res = (gint)log10 (1000000 / ts.tv_nsec);
  398. if (cfg->clock_res < 0) {
  399. cfg->clock_res = 0;
  400. }
  401. if (cfg->clock_res > 3) {
  402. cfg->clock_res = 3;
  403. }
  404. #else
  405. /* For gettimeofday */
  406. cfg->clock_res = 1;
  407. #endif
  408. if ((def_metric = g_hash_table_lookup (cfg->metrics, DEFAULT_METRIC)) == NULL) {
  409. def_metric = check_metric_conf (cfg, NULL);
  410. def_metric->name = DEFAULT_METRIC;
  411. def_metric->actions[METRIC_ACTION_REJECT].score = DEFAULT_SCORE;
  412. cfg->metrics_list = g_list_prepend (cfg->metrics_list, def_metric);
  413. g_hash_table_insert (cfg->metrics, DEFAULT_METRIC, def_metric);
  414. }
  415. cfg->default_metric = def_metric;
  416. /* Lua options */
  417. (void)lua_post_load_config (cfg);
  418. init_dynamic_config (cfg);
  419. }
  420. #if 0
  421. void
  422. parse_err (const gchar *fmt, ...)
  423. {
  424. va_list aq;
  425. gchar logbuf[BUFSIZ], readbuf[32];
  426. gint r;
  427. va_start (aq, fmt);
  428. rspamd_strlcpy (readbuf, yytext, sizeof (readbuf));
  429. r = snprintf (logbuf, sizeof (logbuf), "config file parse error! line: %d, text: %s, reason: ", yylineno, readbuf);
  430. r += vsnprintf (logbuf + r, sizeof (logbuf) - r, fmt, aq);
  431. va_end (aq);
  432. g_critical ("%s", logbuf);
  433. }
  434. void
  435. parse_warn (const gchar *fmt, ...)
  436. {
  437. va_list aq;
  438. gchar logbuf[BUFSIZ], readbuf[32];
  439. gint r;
  440. va_start (aq, fmt);
  441. rspamd_strlcpy (readbuf, yytext, sizeof (readbuf));
  442. r = snprintf (logbuf, sizeof (logbuf), "config file parse warning! line: %d, text: %s, reason: ", yylineno, readbuf);
  443. r += vsnprintf (logbuf + r, sizeof (logbuf) - r, fmt, aq);
  444. va_end (aq);
  445. g_warning ("%s", logbuf);
  446. }
  447. #endif
  448. void
  449. unescape_quotes (gchar *line)
  450. {
  451. gchar *c = line, *t;
  452. while (*c) {
  453. if (*c == '\\' && *(c + 1) == '"') {
  454. t = c;
  455. while (*t) {
  456. *t = *(t + 1);
  457. t++;
  458. }
  459. }
  460. c++;
  461. }
  462. }
  463. GList *
  464. parse_comma_list (memory_pool_t * pool, const gchar *line)
  465. {
  466. GList *res = NULL;
  467. const gchar *c, *p;
  468. gchar *str;
  469. c = line;
  470. p = c;
  471. while (*p) {
  472. if (*p == ',' && *c != *p) {
  473. str = memory_pool_alloc (pool, p - c + 1);
  474. rspamd_strlcpy (str, c, p - c + 1);
  475. res = g_list_prepend (res, str);
  476. /* Skip spaces */
  477. while (g_ascii_isspace (*(++p)));
  478. c = p;
  479. continue;
  480. }
  481. p++;
  482. }
  483. if (res != NULL) {
  484. memory_pool_add_destructor (pool, (pool_destruct_func) g_list_free, res);
  485. }
  486. return res;
  487. }
  488. struct classifier_config *
  489. check_classifier_conf (struct config_file *cfg, struct classifier_config *c)
  490. {
  491. if (c == NULL) {
  492. c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct classifier_config));
  493. }
  494. if (c->opts == NULL) {
  495. c->opts = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  496. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->opts);
  497. }
  498. if (c->labels == NULL) {
  499. c->labels = g_hash_table_new_full (rspamd_str_hash, rspamd_str_equal, NULL, (GDestroyNotify)g_list_free);
  500. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->labels);
  501. }
  502. return c;
  503. }
  504. struct statfile*
  505. check_statfile_conf (struct config_file *cfg, struct statfile *c)
  506. {
  507. if (c == NULL) {
  508. c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct statfile));
  509. }
  510. return c;
  511. }
  512. struct metric *
  513. check_metric_conf (struct config_file *cfg, struct metric *c)
  514. {
  515. int i;
  516. if (c == NULL) {
  517. c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct metric));
  518. c->grow_factor = 1.0;
  519. c->symbols = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  520. c->descriptions = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  521. for (i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i ++) {
  522. c->actions[i].score = -1.0;
  523. }
  524. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->symbols);
  525. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func) g_hash_table_destroy, c->descriptions);
  526. }
  527. return c;
  528. }
  529. struct worker_conf *
  530. check_worker_conf (struct config_file *cfg, struct worker_conf *c)
  531. {
  532. if (c == NULL) {
  533. c = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct worker_conf));
  534. c->params = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  535. c->active_workers = g_queue_new ();
  536. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_hash_table_destroy, c->params);
  537. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_queue_free, c->active_workers);
  538. #ifdef HAVE_SC_NPROCESSORS_ONLN
  539. c->count = sysconf (_SC_NPROCESSORS_ONLN);
  540. #else
  541. c->count = DEFAULT_WORKERS_NUM;
  542. #endif
  543. c->rlimit_nofile = DEFAULT_RLIMIT_NOFILE;
  544. c->rlimit_maxcore = DEFAULT_RLIMIT_MAXCORE;
  545. }
  546. return c;
  547. }
  548. static double
  549. internal_normalizer_func (struct config_file *cfg, long double score, void *data)
  550. {
  551. long double max = *(double *)data;
  552. if (score < 0) {
  553. return score;
  554. }
  555. #ifdef HAVE_TANHL
  556. return max * tanhl (score / max);
  557. #elif defined(HAVE_TANHL)
  558. /*
  559. * As some implementations of libm does not support tanhl, try to use
  560. * tanh
  561. */
  562. return max * tanh ((double) (score / max));
  563. #else
  564. return score < max ? score / max : max;
  565. #endif
  566. }
  567. static gboolean
  568. parse_internal_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line)
  569. {
  570. double *max;
  571. gchar *err;
  572. /* Line contains maximum value for internal normalizer */
  573. max = memory_pool_alloc (cfg->cfg_pool, sizeof (double));
  574. errno = 0;
  575. *max = strtod (line, &err);
  576. if (errno != 0 || *err != '\0') {
  577. msg_err ("cannot parse max number for internal normalizer");
  578. return FALSE;
  579. }
  580. st->normalizer = internal_normalizer_func;
  581. st->normalizer_data = (void *)max;
  582. return TRUE;
  583. }
  584. #ifdef WITH_LUA
  585. static gboolean
  586. parse_lua_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line)
  587. {
  588. gchar *code_begin;
  589. GList *params = NULL;
  590. gint len;
  591. code_begin = strchr (line, ':');
  592. if (code_begin == NULL) {
  593. /* Just function name without code */
  594. params = g_list_prepend (g_list_prepend (NULL, NULL), memory_pool_strdup (cfg->cfg_pool, line));
  595. }
  596. else {
  597. /* Postpone actual code load as lua libraries are not loaded */
  598. /* Put code to list */
  599. params = g_list_prepend (NULL, code_begin + 1);
  600. /* Put function name */
  601. len = code_begin - line;
  602. code_begin = memory_pool_alloc (cfg->cfg_pool, len + 1);
  603. rspamd_strlcpy (code_begin, line, len + 1);
  604. params = g_list_prepend (params, code_begin);
  605. }
  606. memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_list_free, params);
  607. st->normalizer = lua_normalizer_func;
  608. st->normalizer_data = params;
  609. return TRUE;
  610. }
  611. #endif
  612. gboolean
  613. parse_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line)
  614. {
  615. gchar *params_begin;
  616. params_begin = strchr (line, ':');
  617. if (params_begin == NULL) {
  618. msg_err ("no parameters are specified for normalizer %s", line);
  619. return FALSE;
  620. }
  621. /* Try to guess normalizer */
  622. if (g_ascii_strncasecmp (line, "internal", sizeof ("points")) == 0) {
  623. return parse_internal_normalizer (cfg, st, params_begin + 1);
  624. }
  625. #ifdef WITH_LUA
  626. else if (g_ascii_strncasecmp (line, "points", sizeof ("points")) == 0) {
  627. return parse_lua_normalizer (cfg, st, params_begin + 1);
  628. }
  629. #endif
  630. msg_err ("unknown normalizer %s", line);
  631. return FALSE;
  632. }
  633. static GMarkupParser xml_parser = {
  634. .start_element = rspamd_xml_start_element,
  635. .end_element = rspamd_xml_end_element,
  636. .passthrough = NULL,
  637. .text = rspamd_xml_text,
  638. .error = rspamd_xml_error,
  639. };
  640. static const char*
  641. get_filename_extension (const char *filename)
  642. {
  643. const char *dot_pos = strrchr (filename, '.');
  644. if (dot_pos != NULL) {
  645. return (dot_pos + 1);
  646. }
  647. return NULL;
  648. }
  649. /*
  650. * Variables:
  651. * $CONFDIR - configuration directory
  652. * $RUNDIR - local states directory
  653. * $DBDIR - databases dir
  654. * $LOGDIR - logs dir
  655. * $PLUGINSDIR - pluggins dir
  656. * $PREFIX - installation prefix
  657. * $VERSION - rspamd version
  658. */
  659. #define RSPAMD_CONFDIR_MACRO "CONFDIR"
  660. #define RSPAMD_RUNDIR_MACRO "RUNDIR"
  661. #define RSPAMD_DBDIR_MACRO "DBDIR"
  662. #define RSPAMD_LOGDIR_MACRO "LOGDIR"
  663. #define RSPAMD_PLUGINSDIR_MACRO "PLUGINSDIR"
  664. #define RSPAMD_PREFIX_MACRO "PREFIX"
  665. #define RSPAMD_VERSION_MACRO "VERSION"
  666. static void
  667. rspamd_ucl_add_conf_variables (struct ucl_parser *parser)
  668. {
  669. ucl_parser_register_variable (parser, RSPAMD_CONFDIR_MACRO, RSPAMD_CONFDIR);
  670. ucl_parser_register_variable (parser, RSPAMD_RUNDIR_MACRO, RSPAMD_RUNDIR);
  671. ucl_parser_register_variable (parser, RSPAMD_DBDIR_MACRO, RSPAMD_DBDIR);
  672. ucl_parser_register_variable (parser, RSPAMD_LOGDIR_MACRO, RSPAMD_LOGDIR);
  673. ucl_parser_register_variable (parser, RSPAMD_PLUGINSDIR_MACRO, RSPAMD_PLUGINSDIR);
  674. ucl_parser_register_variable (parser, RSPAMD_PREFIX_MACRO, RSPAMD_PREFIX);
  675. ucl_parser_register_variable (parser, RSPAMD_VERSION_MACRO, RVERSION);
  676. }
  677. gboolean
  678. read_rspamd_config (struct config_file *cfg, const gchar *filename, const gchar *convert_to)
  679. {
  680. struct stat st;
  681. gint fd;
  682. gchar *data, *rcl;
  683. const gchar *ext;
  684. GMarkupParseContext *ctx;
  685. GError *err = NULL;
  686. struct rspamd_rcl_section *top;
  687. gboolean res, is_xml = FALSE;
  688. struct rspamd_xml_userdata ud;
  689. struct ucl_parser *parser;
  690. if (stat (filename, &st) == -1) {
  691. msg_err ("cannot stat %s: %s", filename, strerror (errno));
  692. return FALSE;
  693. }
  694. if ((fd = open (filename, O_RDONLY)) == -1) {
  695. msg_err ("cannot open %s: %s", filename, strerror (errno));
  696. return FALSE;
  697. }
  698. /* Now mmap this file to simplify reading process */
  699. if ((data = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
  700. msg_err ("cannot mmap %s: %s", filename, strerror (errno));
  701. close (fd);
  702. return FALSE;
  703. }
  704. close (fd);
  705. if (convert_to != NULL) {
  706. is_xml = TRUE;
  707. }
  708. else {
  709. ext = get_filename_extension (filename);
  710. if (ext != NULL && strcmp (ext, "xml") == 0) {
  711. is_xml = TRUE;
  712. }
  713. }
  714. if (is_xml) {
  715. /* Prepare xml parser */
  716. memset (&ud, 0, sizeof (ud));
  717. ud.cfg = cfg;
  718. ud.state = 0;
  719. ctx = g_markup_parse_context_new (&xml_parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &ud, NULL);
  720. res = g_markup_parse_context_parse (ctx, data, st.st_size, &err);
  721. munmap (data, st.st_size);
  722. }
  723. else {
  724. parser = ucl_parser_new (0);
  725. rspamd_ucl_add_conf_variables (parser);
  726. if (!ucl_parser_add_chunk (parser, data, st.st_size)) {
  727. msg_err ("ucl parser error: %s", ucl_parser_get_error (parser));
  728. munmap (data, st.st_size);
  729. return FALSE;
  730. }
  731. munmap (data, st.st_size);
  732. cfg->rcl_obj = ucl_parser_get_object (parser);
  733. res = TRUE;
  734. }
  735. if (!res) {
  736. return FALSE;
  737. }
  738. if (is_xml && convert_to != NULL) {
  739. /* Convert XML config to UCL */
  740. rcl = ucl_object_emit (cfg->rcl_obj, UCL_EMIT_CONFIG);
  741. if (rcl != NULL) {
  742. fd = open (convert_to, O_CREAT|O_TRUNC|O_WRONLY, 00644);
  743. if (fd == -1) {
  744. msg_err ("cannot open %s: %s", convert_to, strerror (errno));
  745. }
  746. else if (write (fd, rcl, strlen (rcl)) == -1) {
  747. msg_err ("cannot write rcl %s: %s", convert_to, strerror (errno));
  748. }
  749. else {
  750. msg_info ("dumped xml configuration %s to ucl configuration %s",
  751. filename, convert_to);
  752. }
  753. close (fd);
  754. free (rcl);
  755. }
  756. }
  757. top = rspamd_rcl_config_init ();
  758. err = NULL;
  759. if (!rspamd_read_rcl_config (top, cfg, cfg->rcl_obj, &err)) {
  760. msg_err ("rcl parse error: %s", err->message);
  761. return FALSE;
  762. }
  763. return TRUE;
  764. }
  765. static void
  766. symbols_classifiers_callback (gpointer key, gpointer value, gpointer ud)
  767. {
  768. struct config_file *cfg = ud;
  769. register_virtual_symbol (&cfg->cache, key, 1.0);
  770. }
  771. void
  772. insert_classifier_symbols (struct config_file *cfg)
  773. {
  774. g_hash_table_foreach (cfg->classifiers_symbols, symbols_classifiers_callback, cfg);
  775. }
  776. struct classifier_config*
  777. find_classifier_conf (struct config_file *cfg, const gchar *name)
  778. {
  779. GList *cur;
  780. struct classifier_config *cf;
  781. if (name == NULL) {
  782. return NULL;
  783. }
  784. cur = cfg->classifiers;
  785. while (cur) {
  786. cf = cur->data;
  787. if (g_ascii_strcasecmp (cf->classifier->name, name) == 0) {
  788. return cf;
  789. }
  790. cur = g_list_next (cur);
  791. }
  792. return NULL;
  793. }
  794. gboolean
  795. check_classifier_statfiles (struct classifier_config *cf)
  796. {
  797. struct statfile *st;
  798. gboolean has_other = FALSE, res = FALSE, cur_class;
  799. GList *cur;
  800. /* First check classes directly */
  801. cur = cf->statfiles;
  802. while (cur) {
  803. st = cur->data;
  804. if (!has_other) {
  805. cur_class = st->is_spam;
  806. has_other = TRUE;
  807. }
  808. else {
  809. if (cur_class != st->is_spam) {
  810. return TRUE;
  811. }
  812. }
  813. cur = g_list_next (cur);
  814. }
  815. if (!has_other) {
  816. /* We have only one statfile */
  817. return FALSE;
  818. }
  819. /* We have not detected any statfile that has different class, so turn on euristic based on symbol's name */
  820. has_other = FALSE;
  821. cur = cf->statfiles;
  822. while (cur) {
  823. st = cur->data;
  824. if (rspamd_strncasestr (st->symbol, "spam", -1) != NULL) {
  825. st->is_spam = TRUE;
  826. }
  827. else if (rspamd_strncasestr (st->symbol, "ham", -1) != NULL) {
  828. st->is_spam = FALSE;
  829. }
  830. if (!has_other) {
  831. cur_class = st->is_spam;
  832. has_other = TRUE;
  833. }
  834. else {
  835. if (cur_class != st->is_spam) {
  836. res = TRUE;
  837. }
  838. }
  839. cur = g_list_next (cur);
  840. }
  841. return res;
  842. }
  843. /*
  844. * vi:ts=4
  845. */