1 /*-
2  * Copyright 2017 Vsevolod Stakhov
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "config.h"
17 #include "util.h"
18 #include "unix-std.h"
19 
20 #include "ottery.h"
21 #include "cryptobox.h"
22 
23 #ifdef HAVE_TERMIOS_H
24 #include <termios.h>
25 #endif
26 #ifdef HAVE_READPASSPHRASE_H
27 #include <readpassphrase.h>
28 #endif
29 /* libutil */
30 #ifdef HAVE_LIBUTIL_H
31 #include <libutil.h>
32 #endif
33 #ifdef __APPLE__
34 #include <mach/mach_time.h>
35 #include <mach/mach_init.h>
36 #include <mach/thread_act.h>
37 #include <mach/mach_port.h>
38 #endif
39 /* poll */
40 #ifdef HAVE_POLL_H
41 #include <poll.h>
42 #endif
43 
44 #ifdef HAVE_SIGINFO_H
45 #include <siginfo.h>
46 #endif
47 /* sys/wait */
48 #ifdef HAVE_SYS_WAIT_H
49 #include <sys/wait.h>
50 #endif
51 /* sys/resource.h */
52 #ifdef HAVE_SYS_RESOURCE_H
53 #include <sys/resource.h>
54 #endif
55 #ifdef HAVE_RDTSC
56 #ifdef __x86_64__
57 #include <x86intrin.h>
58 #endif
59 #endif
60 
61 #include <math.h> /* for pow */
62 #include <glob.h> /* in fact, we require this file ultimately */
63 
64 #include "zlib.h"
65 #include "contrib/uthash/utlist.h"
66 #include "blas-config.h"
67 
68 /* Check log messages intensity once per minute */
69 #define CHECK_TIME 60
70 /* More than 2 log messages per second */
71 #define BUF_INTENSITY 2
72 /* Default connect timeout for sync sockets */
73 #define CONNECT_TIMEOUT 3
74 
75 /*
76  * Should be defined in a single point
77  */
78 const struct rspamd_controller_pbkdf pbkdf_list[] = {
79 		{
80 				.name = "PBKDF2-blake2b",
81 				.alias = "pbkdf2",
82 				.description = "standard CPU intensive \"slow\" KDF using blake2b hash function",
83 				.type = RSPAMD_CRYPTOBOX_PBKDF2,
84 				.id = RSPAMD_PBKDF_ID_V1,
85 				.complexity = 16000,
86 				.salt_len = 20,
87 				.key_len = rspamd_cryptobox_HASHBYTES / 2
88 		},
89 		{
90 				.name = "Catena-Butterfly",
91 				.alias = "catena",
92 				.description = "modern CPU and memory intensive KDF",
93 				.type = RSPAMD_CRYPTOBOX_CATENA,
94 				.id = RSPAMD_PBKDF_ID_V2,
95 				.complexity = 10,
96 				.salt_len = 20,
97 				.key_len = rspamd_cryptobox_HASHBYTES / 2
98 		}
99 };
100 
101 gint
rspamd_socket_nonblocking(gint fd)102 rspamd_socket_nonblocking (gint fd)
103 {
104 	gint ofl;
105 
106 	ofl = fcntl (fd, F_GETFL, 0);
107 
108 	if (fcntl (fd, F_SETFL, ofl | O_NONBLOCK) == -1) {
109 		return -1;
110 	}
111 	return 0;
112 }
113 
114 gint
rspamd_socket_blocking(gint fd)115 rspamd_socket_blocking (gint fd)
116 {
117 	gint ofl;
118 
119 	ofl = fcntl (fd, F_GETFL, 0);
120 
121 	if (fcntl (fd, F_SETFL, ofl & (~O_NONBLOCK)) == -1) {
122 		return -1;
123 	}
124 	return 0;
125 }
126 
127 gint
rspamd_socket_poll(gint fd,gint timeout,short events)128 rspamd_socket_poll (gint fd, gint timeout, short events)
129 {
130 	gint r;
131 	struct pollfd fds[1];
132 
133 	fds->fd = fd;
134 	fds->events = events;
135 	fds->revents = 0;
136 	while ((r = poll (fds, 1, timeout)) < 0) {
137 		if (errno != EINTR) {
138 			break;
139 		}
140 	}
141 
142 	return r;
143 }
144 
145 gint
rspamd_socket_create(gint af,gint type,gint protocol,gboolean async)146 rspamd_socket_create (gint af, gint type, gint protocol, gboolean async)
147 {
148 	gint fd;
149 
150 	fd = socket (af, type, protocol);
151 	if (fd == -1) {
152 		return -1;
153 	}
154 
155 	/* Set close on exec */
156 	if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) {
157 		close (fd);
158 		return -1;
159 	}
160 	if (async) {
161 		if (rspamd_socket_nonblocking (fd) == -1) {
162 			close (fd);
163 			return -1;
164 		}
165 	}
166 
167 	return fd;
168 }
169 
170 static gint
rspamd_inet_socket_create(gint type,struct addrinfo * addr,gboolean is_server,gboolean async,GList ** list)171 rspamd_inet_socket_create (gint type, struct addrinfo *addr, gboolean is_server,
172 	gboolean async, GList **list)
173 {
174 	gint fd = -1, r, on = 1, s_error;
175 	struct addrinfo *cur;
176 	gpointer ptr;
177 	socklen_t optlen;
178 
179 	cur = addr;
180 	while (cur) {
181 		/* Create socket */
182 		fd = rspamd_socket_create (cur->ai_family, type, cur->ai_protocol, TRUE);
183 		if (fd == -1) {
184 			goto out;
185 		}
186 
187 		if (is_server) {
188 			(void)setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on,
189 					sizeof (gint));
190 #ifdef HAVE_IPV6_V6ONLY
191 			if (cur->ai_family == AF_INET6) {
192 				setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, (const void *)&on,
193 						sizeof (gint));
194 			}
195 #endif
196 			r = bind (fd, cur->ai_addr, cur->ai_addrlen);
197 		}
198 		else {
199 			r = connect (fd, cur->ai_addr, cur->ai_addrlen);
200 		}
201 
202 		if (r == -1) {
203 			if (errno != EINPROGRESS) {
204 				goto out;
205 			}
206 			if (!async) {
207 				/* Try to poll */
208 				if (rspamd_socket_poll (fd, CONNECT_TIMEOUT * 1000,
209 					POLLOUT) <= 0) {
210 					errno = ETIMEDOUT;
211 					goto out;
212 				}
213 				else {
214 					/* Make synced again */
215 					if (rspamd_socket_blocking (fd) < 0) {
216 						goto out;
217 					}
218 				}
219 			}
220 		}
221 		else {
222 			/* Still need to check SO_ERROR on socket */
223 			optlen = sizeof (s_error);
224 
225 			if (getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen) != -1) {
226 				if (s_error) {
227 					errno = s_error;
228 					goto out;
229 				}
230 			}
231 		}
232 		if (list == NULL) {
233 			/* Go out immediately */
234 			break;
235 		}
236 		else if (fd != -1) {
237 			ptr = GINT_TO_POINTER (fd);
238 			*list = g_list_prepend (*list, ptr);
239 			cur = cur->ai_next;
240 			continue;
241 		}
242 out:
243 		if (fd != -1) {
244 			close (fd);
245 		}
246 		fd = -1;
247 		cur = cur->ai_next;
248 	}
249 
250 	return (fd);
251 }
252 
253 gint
rspamd_socket_tcp(struct addrinfo * addr,gboolean is_server,gboolean async)254 rspamd_socket_tcp (struct addrinfo *addr, gboolean is_server, gboolean async)
255 {
256 	return rspamd_inet_socket_create (SOCK_STREAM, addr, is_server, async, NULL);
257 }
258 
259 gint
rspamd_socket_udp(struct addrinfo * addr,gboolean is_server,gboolean async)260 rspamd_socket_udp (struct addrinfo *addr, gboolean is_server, gboolean async)
261 {
262 	return rspamd_inet_socket_create (SOCK_DGRAM, addr, is_server, async, NULL);
263 }
264 
265 gint
rspamd_socket_unix(const gchar * path,struct sockaddr_un * addr,gint type,gboolean is_server,gboolean async)266 rspamd_socket_unix (const gchar *path,
267 	struct sockaddr_un *addr,
268 	gint type,
269 	gboolean is_server,
270 	gboolean async)
271 {
272 
273 	socklen_t optlen;
274 	gint fd = -1, s_error, r, serrno, on = 1;
275 	struct stat st;
276 
277 	if (path == NULL)
278 		return -1;
279 
280 	addr->sun_family = AF_UNIX;
281 
282 	rspamd_strlcpy (addr->sun_path, path, sizeof (addr->sun_path));
283 #ifdef FREEBSD
284 	addr->sun_len = SUN_LEN (addr);
285 #endif
286 
287 	if (is_server) {
288 		/* Unlink socket if it exists already */
289 		if (lstat (addr->sun_path, &st) != -1) {
290 			if (S_ISSOCK (st.st_mode)) {
291 				if (unlink (addr->sun_path) == -1) {
292 					goto out;
293 				}
294 			}
295 			else {
296 				goto out;
297 			}
298 		}
299 	}
300 	fd = socket (PF_LOCAL, type, 0);
301 
302 	if (fd == -1) {
303 		return -1;
304 	}
305 
306 	if (rspamd_socket_nonblocking (fd) < 0) {
307 		goto out;
308 	}
309 
310 	/* Set close on exec */
311 	if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) {
312 		goto out;
313 	}
314 	if (is_server) {
315 		(void)setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on,
316 			sizeof (gint));
317 		r = bind (fd, (struct sockaddr *)addr, SUN_LEN (addr));
318 	}
319 	else {
320 		r = connect (fd, (struct sockaddr *)addr, SUN_LEN (addr));
321 	}
322 
323 	if (r == -1) {
324 		if (errno != EINPROGRESS) {
325 			goto out;
326 		}
327 		if (!async) {
328 			/* Try to poll */
329 			if (rspamd_socket_poll (fd, CONNECT_TIMEOUT * 1000, POLLOUT) <= 0) {
330 				errno = ETIMEDOUT;
331 				goto out;
332 			}
333 			else {
334 				/* Make synced again */
335 				if (rspamd_socket_blocking (fd) < 0) {
336 					goto out;
337 				}
338 			}
339 		}
340 	}
341 	else {
342 		/* Still need to check SO_ERROR on socket */
343 		optlen = sizeof (s_error);
344 
345 		if (getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen) != -1) {
346 			if (s_error) {
347 				errno = s_error;
348 				goto out;
349 			}
350 		}
351 	}
352 
353 
354 	return (fd);
355 
356 out:
357 	serrno = errno;
358 	if (fd != -1) {
359 		close (fd);
360 	}
361 	errno = serrno;
362 	return (-1);
363 }
364 
365 static int
rspamd_prefer_v4_hack(const struct addrinfo * a1,const struct addrinfo * a2)366 rspamd_prefer_v4_hack (const struct addrinfo *a1, const struct addrinfo *a2)
367 {
368 	return a1->ai_addr->sa_family - a2->ai_addr->sa_family;
369 }
370 
371 /**
372  * Make a universal socket
373  * @param credits host, ip or path to unix socket
374  * @param port port (used for network sockets)
375  * @param async make this socket asynced
376  * @param is_server make this socket as server socket
377  * @param try_resolve try name resolution for a socket (BLOCKING)
378  */
379 gint
rspamd_socket(const gchar * credits,guint16 port,gint type,gboolean async,gboolean is_server,gboolean try_resolve)380 rspamd_socket (const gchar *credits, guint16 port,
381 	gint type, gboolean async, gboolean is_server, gboolean try_resolve)
382 {
383 	struct sockaddr_un un;
384 	struct stat st;
385 	struct addrinfo hints, *res;
386 	gint r;
387 	gchar portbuf[8];
388 
389 	if (*credits == '/') {
390 		if (is_server) {
391 			return rspamd_socket_unix (credits, &un, type, is_server, async);
392 		}
393 		else {
394 			r = stat (credits, &st);
395 			if (r == -1) {
396 				/* Unix socket doesn't exists it must be created first */
397 				errno = ENOENT;
398 				return -1;
399 			}
400 			else {
401 				if ((st.st_mode & S_IFSOCK) == 0) {
402 					/* Path is not valid socket */
403 					errno = EINVAL;
404 					return -1;
405 				}
406 				else {
407 					return rspamd_socket_unix (credits,
408 							   &un,
409 							   type,
410 							   is_server,
411 							   async);
412 				}
413 			}
414 		}
415 	}
416 	else {
417 		/* TCP related part */
418 		memset (&hints, 0, sizeof (hints));
419 		hints.ai_family = AF_UNSPEC;     /* Allow IPv4 or IPv6 */
420 		hints.ai_socktype = type; /* Type of the socket */
421 		hints.ai_flags = is_server ? AI_PASSIVE : 0;
422 		hints.ai_protocol = 0;           /* Any protocol */
423 		hints.ai_canonname = NULL;
424 		hints.ai_addr = NULL;
425 		hints.ai_next = NULL;
426 
427 		if (!try_resolve) {
428 			hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
429 		}
430 
431 		rspamd_snprintf (portbuf, sizeof (portbuf), "%d", (int)port);
432 		if ((r = getaddrinfo (credits, portbuf, &hints, &res)) == 0) {
433 			LL_SORT2 (res, rspamd_prefer_v4_hack, ai_next);
434 			r = rspamd_inet_socket_create (type, res, is_server, async, NULL);
435 			freeaddrinfo (res);
436 			return r;
437 		}
438 		else {
439 			return -1;
440 		}
441 	}
442 }
443 
444 gboolean
rspamd_socketpair(gint pair[2],gint af)445 rspamd_socketpair (gint pair[2], gint af)
446 {
447 	gint r = -1, serrno;
448 
449 #ifdef HAVE_SOCK_SEQPACKET
450 	if (af == SOCK_SEQPACKET) {
451 		r = socketpair (AF_LOCAL, SOCK_SEQPACKET, 0, pair);
452 
453 		if (r == -1) {
454 			r = socketpair (AF_LOCAL, SOCK_DGRAM, 0, pair);
455 		}
456 	}
457 #endif
458 	if (r == -1) {
459 		r = socketpair (AF_LOCAL, af, 0, pair);
460 	}
461 
462 	if (r == -1) {
463 		return -1;
464 	}
465 
466 	/* Set close on exec */
467 	if (fcntl (pair[0], F_SETFD, FD_CLOEXEC) == -1) {
468 		goto out;
469 	}
470 	if (fcntl (pair[1], F_SETFD, FD_CLOEXEC) == -1) {
471 		goto out;
472 	}
473 
474 	return TRUE;
475 
476 out:
477 	serrno = errno;
478 	close (pair[0]);
479 	close (pair[1]);
480 	errno = serrno;
481 
482 	return FALSE;
483 }
484 
485 #ifdef HAVE_SA_SIGINFO
486 void
rspamd_signals_init(struct sigaction * signals,void (* sig_handler)(gint,siginfo_t *,void *))487 rspamd_signals_init (struct sigaction *signals, void (*sig_handler)(gint,
488 	siginfo_t *,
489 	void *))
490 #else
491 void
492 rspamd_signals_init (struct sigaction *signals, void (*sig_handler)(gint))
493 #endif
494 {
495 	struct sigaction sigpipe_act;
496 	/* Setting up signal handlers */
497 	/* SIGUSR1 - reopen config file */
498 	/* SIGUSR2 - worker is ready for accept */
499 	sigemptyset (&signals->sa_mask);
500 	sigaddset (&signals->sa_mask, SIGTERM);
501 	sigaddset (&signals->sa_mask, SIGINT);
502 	sigaddset (&signals->sa_mask, SIGHUP);
503 	sigaddset (&signals->sa_mask, SIGCHLD);
504 	sigaddset (&signals->sa_mask, SIGUSR1);
505 	sigaddset (&signals->sa_mask, SIGUSR2);
506 	sigaddset (&signals->sa_mask, SIGALRM);
507 #ifdef SIGPOLL
508 	sigaddset (&signals->sa_mask, SIGPOLL);
509 #endif
510 #ifdef SIGIO
511 	sigaddset (&signals->sa_mask, SIGIO);
512 #endif
513 
514 #ifdef HAVE_SA_SIGINFO
515 	signals->sa_flags = SA_SIGINFO;
516 	signals->sa_handler = NULL;
517 	signals->sa_sigaction = sig_handler;
518 #else
519 	signals->sa_handler = sig_handler;
520 	signals->sa_flags = 0;
521 #endif
522 	sigaction (SIGTERM, signals, NULL);
523 	sigaction (SIGINT,	signals, NULL);
524 	sigaction (SIGHUP,	signals, NULL);
525 	sigaction (SIGCHLD, signals, NULL);
526 	sigaction (SIGUSR1, signals, NULL);
527 	sigaction (SIGUSR2, signals, NULL);
528 	sigaction (SIGALRM, signals, NULL);
529 #ifdef SIGPOLL
530 	sigaction (SIGPOLL, signals, NULL);
531 #endif
532 #ifdef SIGIO
533 	sigaction (SIGIO, signals, NULL);
534 #endif
535 
536 	/* Ignore SIGPIPE as we handle write errors manually */
537 	sigemptyset (&sigpipe_act.sa_mask);
538 	sigaddset (&sigpipe_act.sa_mask, SIGPIPE);
539 	sigpipe_act.sa_handler = SIG_IGN;
540 	sigpipe_act.sa_flags = 0;
541 	sigaction (SIGPIPE, &sigpipe_act, NULL);
542 }
543 
544 #ifndef HAVE_SETPROCTITLE
545 
546 #ifdef LINUX
547 static gchar *title_buffer = NULL;
548 static size_t title_buffer_size = 0;
549 static gchar *title_progname, *title_progname_full;
550 #endif
551 
552 #ifdef LINUX
553 static void
rspamd_title_dtor(gpointer d)554 rspamd_title_dtor (gpointer d)
555 {
556 	gchar **env = (gchar **)d;
557 	guint i;
558 
559 	for (i = 0; env[i] != NULL; i++) {
560 		g_free (env[i]);
561 	}
562 
563 	g_free (env);
564 }
565 #endif
566 
567 gint
init_title(rspamd_mempool_t * pool,gint argc,gchar * argv[],gchar * envp[])568 init_title (rspamd_mempool_t *pool,
569 		gint argc, gchar *argv[], gchar *envp[])
570 {
571 #ifdef LINUX
572 	gchar *begin_of_buffer = 0, *end_of_buffer = 0;
573 	gint i;
574 
575 	for (i = 0; i < argc; ++i) {
576 		if (!begin_of_buffer) {
577 			begin_of_buffer = argv[i];
578 		}
579 		if (!end_of_buffer || end_of_buffer + 1 == argv[i]) {
580 			end_of_buffer = argv[i] + strlen (argv[i]);
581 		}
582 	}
583 
584 	for (i = 0; envp[i]; ++i) {
585 		if (!begin_of_buffer) {
586 			begin_of_buffer = envp[i];
587 		}
588 		if (!end_of_buffer || end_of_buffer + 1 == envp[i]) {
589 			end_of_buffer = envp[i] + strlen (envp[i]);
590 		}
591 	}
592 
593 	if (!end_of_buffer) {
594 		return 0;
595 	}
596 
597 	gchar **new_environ = g_malloc ((i + 1) * sizeof (envp[0]));
598 
599 	for (i = 0; envp[i]; ++i) {
600 		new_environ[i] = g_strdup (envp[i]);
601 	}
602 
603 	new_environ[i] = NULL;
604 
605 	if (program_invocation_name) {
606 		title_progname_full = g_strdup (program_invocation_name);
607 
608 		gchar *p = strrchr (title_progname_full, '/');
609 
610 		if (p) {
611 			title_progname = p + 1;
612 		}
613 		else {
614 			title_progname = title_progname_full;
615 		}
616 
617 		program_invocation_name = title_progname_full;
618 		program_invocation_short_name = title_progname;
619 	}
620 
621 	environ = new_environ;
622 	title_buffer = begin_of_buffer;
623 	title_buffer_size = end_of_buffer - begin_of_buffer;
624 
625 	rspamd_mempool_add_destructor (pool,
626 			rspamd_title_dtor, new_environ);
627 #endif
628 
629 	return 0;
630 }
631 
632 gint
setproctitle(const gchar * fmt,...)633 setproctitle (const gchar *fmt, ...)
634 {
635 #if defined(LINUX)
636 	if (!title_buffer || !title_buffer_size) {
637 		errno = ENOMEM;
638 		return -1;
639 	}
640 
641 	memset (title_buffer, '\0', title_buffer_size);
642 
643 	ssize_t written;
644 
645 	if (fmt) {
646 		va_list ap;
647 
648 		written = rspamd_snprintf (title_buffer,
649 				title_buffer_size,
650 				"%s: ",
651 				title_progname);
652 		if (written < 0 || (size_t) written >= title_buffer_size)
653 			return -1;
654 
655 		va_start (ap, fmt);
656 		rspamd_vsnprintf (title_buffer + written,
657 				title_buffer_size - written,
658 				fmt,
659 				ap);
660 		va_end (ap);
661 	}
662 	else {
663 		written = rspamd_snprintf (title_buffer,
664 				title_buffer_size,
665 				"%s",
666 				title_progname);
667 		if (written < 0 || (size_t) written >= title_buffer_size)
668 			return -1;
669 	}
670 
671 	written = strlen (title_buffer);
672 	memset (title_buffer + written, '\0', title_buffer_size - written);
673 #elif defined(__APPLE__)
674 	/* OSX is broken, ignore this brain damaged system */
675 #else
676 	/* Last resort (usually broken, but eh...) */
677 	GString *dest;
678 	va_list ap;
679 
680 	dest = g_string_new ("");
681 	va_start (ap, fmt);
682 	rspamd_vprintf_gstring (dest, fmt, ap);
683 	va_end (ap);
684 
685 	g_set_prgname (dest->str);
686 	g_string_free (dest, TRUE);
687 
688 #endif
689 	return 0;
690 }
691 
692 #endif
693 
694 #ifndef HAVE_PIDFILE
695 static gint _rspamd_pidfile_remove (rspamd_pidfh_t *pfh, gint freeit);
696 
697 static gint
rspamd_pidfile_verify(rspamd_pidfh_t * pfh)698 rspamd_pidfile_verify (rspamd_pidfh_t *pfh)
699 {
700 	struct stat sb;
701 
702 	if (pfh == NULL || pfh->pf_fd == -1)
703 		return (-1);
704 	/*
705 	 * Check remembered descriptor.
706 	 */
707 	if (fstat (pfh->pf_fd, &sb) == -1)
708 		return (errno);
709 	if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino)
710 		return -1;
711 	return 0;
712 }
713 
714 static gint
rspamd_pidfile_read(const gchar * path,pid_t * pidptr)715 rspamd_pidfile_read (const gchar *path, pid_t * pidptr)
716 {
717 	gchar buf[16], *endptr;
718 	gint error, fd, i;
719 
720 	fd = open (path, O_RDONLY);
721 	if (fd == -1)
722 		return (errno);
723 
724 	i = read (fd, buf, sizeof (buf) - 1);
725 	error = errno;              /* Remember errno in case close() wants to change it. */
726 	close (fd);
727 	if (i == -1)
728 		return error;
729 	else if (i == 0)
730 		return EAGAIN;
731 	buf[i] = '\0';
732 
733 	*pidptr = strtol (buf, &endptr, 10);
734 	if (endptr != &buf[i])
735 		return EINVAL;
736 
737 	return 0;
738 }
739 
740 rspamd_pidfh_t *
rspamd_pidfile_open(const gchar * path,mode_t mode,pid_t * pidptr)741 rspamd_pidfile_open (const gchar *path, mode_t mode, pid_t * pidptr)
742 {
743 	rspamd_pidfh_t *pfh;
744 	struct stat sb;
745 	gint error, fd, len, count;
746 	struct timespec rqtp;
747 
748 	pfh = g_malloc (sizeof (*pfh));
749 	if (pfh == NULL)
750 		return NULL;
751 
752 	if (path == NULL)
753 		len = snprintf (pfh->pf_path,
754 				sizeof (pfh->pf_path),
755 				"/var/run/%s.pid",
756 				g_get_prgname ());
757 	else
758 		len = snprintf (pfh->pf_path, sizeof (pfh->pf_path), "%s", path);
759 	if (len >= (gint)sizeof (pfh->pf_path)) {
760 		g_free (pfh);
761 		errno = ENAMETOOLONG;
762 		return NULL;
763 	}
764 
765 	/*
766 	 * Open the PID file and obtain exclusive lock.
767 	 * We truncate PID file here only to remove old PID immediatelly,
768 	 * PID file will be truncated again in pidfile_write(), so
769 	 * pidfile_write() can be called multiple times.
770 	 */
771 	fd = open (pfh->pf_path, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode);
772 	rspamd_file_lock (fd, TRUE);
773 	if (fd == -1) {
774 		count = 0;
775 		rqtp.tv_sec = 0;
776 		rqtp.tv_nsec = 5000000;
777 		if (errno == EWOULDBLOCK && pidptr != NULL) {
778 again:
779 			errno = rspamd_pidfile_read (pfh->pf_path, pidptr);
780 			if (errno == 0)
781 				errno = EEXIST;
782 			else if (errno == EAGAIN) {
783 				if (++count <= 3) {
784 					nanosleep (&rqtp, 0);
785 					goto again;
786 				}
787 			}
788 		}
789 		g_free (pfh);
790 		return NULL;
791 	}
792 	/*
793 	 * Remember file information, so in pidfile_write() we are sure we write
794 	 * to the proper descriptor.
795 	 */
796 	if (fstat (fd, &sb) == -1) {
797 		error = errno;
798 		unlink (pfh->pf_path);
799 		close (fd);
800 		g_free (pfh);
801 		errno = error;
802 		return NULL;
803 	}
804 
805 	pfh->pf_fd = fd;
806 	pfh->pf_dev = sb.st_dev;
807 	pfh->pf_ino = sb.st_ino;
808 
809 	return pfh;
810 }
811 
812 gint
rspamd_pidfile_write(rspamd_pidfh_t * pfh)813 rspamd_pidfile_write (rspamd_pidfh_t *pfh)
814 {
815 	gchar pidstr[16];
816 	gint error, fd;
817 
818 	/*
819 	 * Check remembered descriptor, so we don't overwrite some other
820 	 * file if pidfile was closed and descriptor reused.
821 	 */
822 	errno = rspamd_pidfile_verify (pfh);
823 	if (errno != 0) {
824 		/*
825 		 * Don't close descriptor, because we are not sure if it's ours.
826 		 */
827 		return -1;
828 	}
829 	fd = pfh->pf_fd;
830 
831 	/*
832 	 * Truncate PID file, so multiple calls of pidfile_write() are allowed.
833 	 */
834 	if (ftruncate (fd, 0) == -1) {
835 		error = errno;
836 		_rspamd_pidfile_remove (pfh, 0);
837 		errno = error;
838 		return -1;
839 	}
840 
841 	rspamd_snprintf (pidstr, sizeof (pidstr), "%P", getpid ());
842 	if (pwrite (fd, pidstr, strlen (pidstr), 0) != (ssize_t) strlen (pidstr)) {
843 		error = errno;
844 		_rspamd_pidfile_remove (pfh, 0);
845 		errno = error;
846 		return -1;
847 	}
848 
849 	return 0;
850 }
851 
852 gint
rspamd_pidfile_close(rspamd_pidfh_t * pfh)853 rspamd_pidfile_close (rspamd_pidfh_t *pfh)
854 {
855 	gint error;
856 
857 	error = rspamd_pidfile_verify (pfh);
858 	if (error != 0) {
859 		errno = error;
860 		return -1;
861 	}
862 
863 	if (close (pfh->pf_fd) == -1)
864 		error = errno;
865 	g_free (pfh);
866 	if (error != 0) {
867 		errno = error;
868 		return -1;
869 	}
870 	return 0;
871 }
872 
873 static gint
_rspamd_pidfile_remove(rspamd_pidfh_t * pfh,gint freeit)874 _rspamd_pidfile_remove (rspamd_pidfh_t *pfh, gint freeit)
875 {
876 	gint error;
877 
878 	error = rspamd_pidfile_verify (pfh);
879 	if (error != 0) {
880 		errno = error;
881 		return -1;
882 	}
883 
884 	if (unlink (pfh->pf_path) == -1)
885 		error = errno;
886 	if (!rspamd_file_unlock (pfh->pf_fd, FALSE)) {
887 		if (error == 0)
888 			error = errno;
889 	}
890 	if (close (pfh->pf_fd) == -1) {
891 		if (error == 0)
892 			error = errno;
893 	}
894 	if (freeit)
895 		g_free (pfh);
896 	else
897 		pfh->pf_fd = -1;
898 	if (error != 0) {
899 		errno = error;
900 		return -1;
901 	}
902 	return 0;
903 }
904 
905 gint
rspamd_pidfile_remove(rspamd_pidfh_t * pfh)906 rspamd_pidfile_remove (rspamd_pidfh_t *pfh)
907 {
908 
909 	return (_rspamd_pidfile_remove (pfh, 1));
910 }
911 #endif
912 
913 /* Replace %r with rcpt value and %f with from value, new string is allocated in pool */
914 gchar *
resolve_stat_filename(rspamd_mempool_t * pool,gchar * pattern,gchar * rcpt,gchar * from)915 resolve_stat_filename (rspamd_mempool_t * pool,
916 	gchar *pattern,
917 	gchar *rcpt,
918 	gchar *from)
919 {
920 	gint need_to_format = 0, len = 0;
921 	gint rcptlen, fromlen;
922 	gchar *c = pattern, *new, *s;
923 
924 	if (rcpt) {
925 		rcptlen = strlen (rcpt);
926 	}
927 	else {
928 		rcptlen = 0;
929 	}
930 
931 	if (from) {
932 		fromlen = strlen (from);
933 	}
934 	else {
935 		fromlen = 0;
936 	}
937 
938 	/* Calculate length */
939 	while (*c++) {
940 		if (*c == '%' && *(c + 1) == 'r') {
941 			len += rcptlen;
942 			c += 2;
943 			need_to_format = 1;
944 			continue;
945 		}
946 		else if (*c == '%' && *(c + 1) == 'f') {
947 			len += fromlen;
948 			c += 2;
949 			need_to_format = 1;
950 			continue;
951 		}
952 		len++;
953 	}
954 
955 	/* Do not allocate extra memory if we do not need to format string */
956 	if (!need_to_format) {
957 		return pattern;
958 	}
959 
960 	/* Allocate new string */
961 	new = rspamd_mempool_alloc (pool, len);
962 	c = pattern;
963 	s = new;
964 
965 	/* Format string */
966 	while (*c++) {
967 		if (*c == '%' && *(c + 1) == 'r') {
968 			c += 2;
969 			memcpy (s, rcpt, rcptlen);
970 			s += rcptlen;
971 			continue;
972 		}
973 		*s++ = *c;
974 	}
975 
976 	*s = '\0';
977 
978 	return new;
979 }
980 
981 const gchar *
rspamd_log_check_time(gdouble start,gdouble end,gint resolution)982 rspamd_log_check_time (gdouble start, gdouble end, gint resolution)
983 {
984 	gdouble diff;
985 	static gchar res[64];
986 	gchar fmt[32];
987 
988 	diff = (end - start) * 1000.0;
989 
990 	rspamd_snprintf (fmt, sizeof (fmt), "%%.%dfms", resolution);
991 	rspamd_snprintf (res, sizeof (res), fmt, diff);
992 
993 	return (const gchar *)res;
994 }
995 
996 
997 #ifdef HAVE_FLOCK
998 /* Flock version */
999 gboolean
rspamd_file_lock(gint fd,gboolean async)1000 rspamd_file_lock (gint fd, gboolean async)
1001 {
1002 	gint flags;
1003 
1004 	if (async) {
1005 		flags = LOCK_EX | LOCK_NB;
1006 	}
1007 	else {
1008 		flags = LOCK_EX;
1009 	}
1010 
1011 	if (flock (fd, flags) == -1) {
1012 		if (async && errno == EAGAIN) {
1013 			return FALSE;
1014 		}
1015 
1016 		return FALSE;
1017 	}
1018 
1019 	return TRUE;
1020 }
1021 
1022 gboolean
rspamd_file_unlock(gint fd,gboolean async)1023 rspamd_file_unlock (gint fd, gboolean async)
1024 {
1025 	gint flags;
1026 
1027 	if (async) {
1028 		flags = LOCK_UN | LOCK_NB;
1029 	}
1030 	else {
1031 		flags = LOCK_UN;
1032 	}
1033 
1034 	if (flock (fd, flags) == -1) {
1035 		if (async && errno == EAGAIN) {
1036 			return FALSE;
1037 		}
1038 
1039 		return FALSE;
1040 	}
1041 
1042 	return TRUE;
1043 
1044 }
1045 #else /* HAVE_FLOCK */
1046 /* Fctnl version */
1047 gboolean
rspamd_file_lock(gint fd,gboolean async)1048 rspamd_file_lock (gint fd, gboolean async)
1049 {
1050 	struct flock fl = {
1051 		.l_type = F_WRLCK,
1052 		.l_whence = SEEK_SET,
1053 		.l_start = 0,
1054 		.l_len = 0
1055 	};
1056 
1057 	if (fcntl (fd, async ? F_SETLK : F_SETLKW, &fl) == -1) {
1058 		if (async && (errno == EAGAIN || errno == EACCES)) {
1059 			return FALSE;
1060 		}
1061 
1062 		return FALSE;
1063 	}
1064 
1065 	return TRUE;
1066 }
1067 
1068 gboolean
rspamd_file_unlock(gint fd,gboolean async)1069 rspamd_file_unlock (gint fd, gboolean async)
1070 {
1071 	struct flock fl = {
1072 		.l_type = F_UNLCK,
1073 		.l_whence = SEEK_SET,
1074 		.l_start = 0,
1075 		.l_len = 0
1076 	};
1077 
1078 	if (fcntl (fd, async ? F_SETLK : F_SETLKW, &fl) == -1) {
1079 		if (async && (errno == EAGAIN || errno == EACCES)) {
1080 			return FALSE;
1081 		}
1082 
1083 		return FALSE;
1084 	}
1085 
1086 	return TRUE;
1087 
1088 }
1089 #endif /* HAVE_FLOCK */
1090 
1091 
1092 #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 22))
1093 void
g_ptr_array_unref(GPtrArray * array)1094 g_ptr_array_unref (GPtrArray *array)
1095 {
1096 	g_ptr_array_free (array, TRUE);
1097 }
1098 gboolean
g_int64_equal(gconstpointer v1,gconstpointer v2)1099 g_int64_equal (gconstpointer v1, gconstpointer v2)
1100 {
1101 	return *((const gint64*) v1) == *((const gint64*) v2);
1102 }
1103 guint
g_int64_hash(gconstpointer v)1104 g_int64_hash (gconstpointer v)
1105 {
1106 	guint64 v64 = *(guint64 *)v;
1107 
1108 	return (guint) (v ^ (v >> 32));
1109 }
1110 #endif
1111 #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 14))
1112 void
g_queue_clear(GQueue * queue)1113 g_queue_clear (GQueue *queue)
1114 {
1115 	g_return_if_fail (queue != NULL);
1116 
1117 	g_list_free (queue->head);
1118 	queue->head = queue->tail = NULL;
1119 	queue->length = 0;
1120 }
1121 #endif
1122 #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 30))
1123 GPtrArray*
g_ptr_array_new_full(guint reserved_size,GDestroyNotify element_free_func)1124 g_ptr_array_new_full (guint reserved_size,
1125 		GDestroyNotify element_free_func)
1126 {
1127 	GPtrArray *array;
1128 
1129 	array = g_ptr_array_sized_new (reserved_size);
1130 	g_ptr_array_set_free_func (array, element_free_func);
1131 
1132 	return array;
1133 }
1134 #endif
1135 #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 32))
1136 void
g_queue_free_full(GQueue * queue,GDestroyNotify free_func)1137 g_queue_free_full (GQueue *queue, GDestroyNotify free_func)
1138 {
1139 	GList *cur;
1140 
1141 	cur = queue->head;
1142 
1143 	while (cur) {
1144 		free_func (cur->data);
1145 		cur = g_list_next (cur);
1146 	}
1147 
1148 	g_queue_free (queue);
1149 }
1150 #endif
1151 
1152 #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 40))
1153 void
g_ptr_array_insert(GPtrArray * array,gint index_,gpointer data)1154 g_ptr_array_insert (GPtrArray *array, gint index_, gpointer data)
1155 {
1156 	g_return_if_fail (array);
1157 	g_return_if_fail (index_ >= -1);
1158 	g_return_if_fail (index_ <= (gint )array->len);
1159 
1160 	g_ptr_array_set_size (array, array->len + 1);
1161 
1162 	if (index_ < 0) {
1163 		index_ = array->len;
1164 	}
1165 
1166 	if (index_ < array->len) {
1167 		memmove (&(array->pdata[index_ + 1]), &(array->pdata[index_]),
1168 				(array->len - index_) * sizeof(gpointer));
1169 	}
1170 
1171 	array->pdata[index_] = data;
1172 }
1173 #endif
1174 
1175 #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 32))
1176 const gchar *
g_environ_getenv(gchar ** envp,const gchar * variable)1177 g_environ_getenv (gchar **envp, const gchar *variable)
1178 {
1179 	gsize len;
1180 	gint i;
1181 
1182 	if (envp == NULL) {
1183 		return NULL;
1184 	}
1185 
1186 	len = strlen (variable);
1187 
1188 	for (i = 0; envp[i]; i++) {
1189 		if (strncmp (envp[i], variable, len) == 0 && envp[i][len] == '=') {
1190 			return envp[i] + len + 1;
1191 		}
1192 	}
1193 
1194 	return NULL;
1195 }
1196 #endif
1197 
1198 gint
rspamd_fallocate(gint fd,off_t offset,off_t len)1199 rspamd_fallocate (gint fd, off_t offset, off_t len)
1200 {
1201 #if defined(HAVE_FALLOCATE)
1202 	return fallocate (fd, 0, offset, len);
1203 #elif defined(HAVE_POSIX_FALLOCATE)
1204 	return posix_fallocate (fd, offset, len);
1205 #else
1206 	/* Return 0 as nothing can be done on this system */
1207 	return 0;
1208 #endif
1209 }
1210 
1211 
1212 /**
1213  * Create new mutex
1214  * @return mutex or NULL
1215  */
1216 inline rspamd_mutex_t *
rspamd_mutex_new(void)1217 rspamd_mutex_new (void)
1218 {
1219 	rspamd_mutex_t *new;
1220 
1221 	new = g_malloc0 (sizeof (rspamd_mutex_t));
1222 #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30))
1223 	g_mutex_init (&new->mtx);
1224 #else
1225 	g_static_mutex_init (&new->mtx);
1226 #endif
1227 
1228 	return new;
1229 }
1230 
1231 /**
1232  * Lock mutex
1233  * @param mtx
1234  */
1235 inline void
rspamd_mutex_lock(rspamd_mutex_t * mtx)1236 rspamd_mutex_lock (rspamd_mutex_t *mtx)
1237 {
1238 #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30))
1239 	g_mutex_lock (&mtx->mtx);
1240 #else
1241 	g_static_mutex_lock (&mtx->mtx);
1242 #endif
1243 }
1244 
1245 /**
1246  * Unlock mutex
1247  * @param mtx
1248  */
1249 inline void
rspamd_mutex_unlock(rspamd_mutex_t * mtx)1250 rspamd_mutex_unlock (rspamd_mutex_t *mtx)
1251 {
1252 #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30))
1253 	g_mutex_unlock (&mtx->mtx);
1254 #else
1255 	g_static_mutex_unlock (&mtx->mtx);
1256 #endif
1257 }
1258 
1259 void
rspamd_mutex_free(rspamd_mutex_t * mtx)1260 rspamd_mutex_free (rspamd_mutex_t *mtx)
1261 {
1262 #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30))
1263 	g_mutex_clear (&mtx->mtx);
1264 #endif
1265 	g_free (mtx);
1266 }
1267 
1268 struct rspamd_thread_data {
1269 	gchar *name;
1270 	gint id;
1271 	GThreadFunc func;
1272 	gpointer data;
1273 };
1274 
1275 static gpointer
rspamd_thread_func(gpointer ud)1276 rspamd_thread_func (gpointer ud)
1277 {
1278 	struct rspamd_thread_data *td = ud;
1279 	sigset_t s_mask;
1280 
1281 	/* Ignore signals in thread */
1282 	sigemptyset (&s_mask);
1283 	sigaddset (&s_mask, SIGINT);
1284 	sigaddset (&s_mask, SIGHUP);
1285 	sigaddset (&s_mask, SIGCHLD);
1286 	sigaddset (&s_mask, SIGUSR1);
1287 	sigaddset (&s_mask, SIGUSR2);
1288 	sigaddset (&s_mask, SIGALRM);
1289 	sigaddset (&s_mask, SIGPIPE);
1290 
1291 	pthread_sigmask (SIG_BLOCK, &s_mask, NULL);
1292 
1293 	ud = td->func (td->data);
1294 	g_free (td->name);
1295 	g_free (td);
1296 
1297 	return ud;
1298 }
1299 
1300 struct hash_copy_callback_data {
1301 	gpointer (*key_copy_func)(gconstpointer data, gpointer ud);
1302 	gpointer (*value_copy_func)(gconstpointer data, gpointer ud);
1303 	gpointer ud;
1304 	GHashTable *dst;
1305 };
1306 
1307 static void
copy_foreach_callback(gpointer key,gpointer value,gpointer ud)1308 copy_foreach_callback (gpointer key, gpointer value, gpointer ud)
1309 {
1310 	struct hash_copy_callback_data *cb = ud;
1311 	gpointer nkey, nvalue;
1312 
1313 	nkey = cb->key_copy_func ? cb->key_copy_func (key, cb->ud) : (gpointer)key;
1314 	nvalue =
1315 		cb->value_copy_func ? cb->value_copy_func (value,
1316 			cb->ud) : (gpointer)value;
1317 	g_hash_table_insert (cb->dst, nkey, nvalue);
1318 }
1319 /**
1320  * Deep copy of one hash table to another
1321  * @param src source hash
1322  * @param dst destination hash
1323  * @param key_copy_func function called to copy or modify keys (or NULL)
1324  * @param value_copy_func function called to copy or modify values (or NULL)
1325  * @param ud user data for copy functions
1326  */
1327 void
rspamd_hash_table_copy(GHashTable * src,GHashTable * dst,gpointer (* key_copy_func)(gconstpointer data,gpointer ud),gpointer (* value_copy_func)(gconstpointer data,gpointer ud),gpointer ud)1328 rspamd_hash_table_copy (GHashTable *src, GHashTable *dst,
1329 	gpointer (*key_copy_func)(gconstpointer data, gpointer ud),
1330 	gpointer (*value_copy_func)(gconstpointer data, gpointer ud),
1331 	gpointer ud)
1332 {
1333 	struct hash_copy_callback_data cb;
1334 	if (src != NULL && dst != NULL) {
1335 		cb.key_copy_func = key_copy_func;
1336 		cb.value_copy_func = value_copy_func;
1337 		cb.ud = ud;
1338 		cb.dst = dst;
1339 		g_hash_table_foreach (src, copy_foreach_callback, &cb);
1340 	}
1341 }
1342 
1343 static volatile sig_atomic_t saved_signo[NSIG];
1344 
1345 static
1346 void
read_pass_tmp_sig_handler(int s)1347 read_pass_tmp_sig_handler (int s)
1348 {
1349 
1350 	saved_signo[s] = 1;
1351 }
1352 
1353 #ifndef _PATH_TTY
1354 # define _PATH_TTY "/dev/tty"
1355 #endif
1356 
1357 gint
rspamd_read_passphrase(gchar * buf,gint size,gint rwflag,gpointer key)1358 rspamd_read_passphrase (gchar *buf, gint size, gint rwflag, gpointer key)
1359 {
1360 #ifdef HAVE_READPASSPHRASE_H
1361 	if (readpassphrase ("Enter passphrase: ", buf, size, RPP_ECHO_OFF |
1362 		RPP_REQUIRE_TTY) == NULL) {
1363 		return 0;
1364 	}
1365 
1366 	return strlen (buf);
1367 #else
1368 	struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
1369 	struct sigaction savetstp, savettin, savettou, savepipe;
1370 	struct termios term, oterm;
1371 	gint input, output, i;
1372 	gchar *end, *p, ch;
1373 
1374 restart:
1375 	if ((input = output = open (_PATH_TTY, O_RDWR)) == -1) {
1376 		errno = ENOTTY;
1377 		return 0;
1378 	}
1379 
1380 	(void)fcntl (input, F_SETFD, FD_CLOEXEC);
1381 
1382 	/* Turn echo off */
1383 	if (tcgetattr (input, &oterm) != 0) {
1384 		close (input);
1385 		errno = ENOTTY;
1386 		return 0;
1387 	}
1388 
1389 	memcpy (&term, &oterm, sizeof(term));
1390 	term.c_lflag &= ~(ECHO | ECHONL);
1391 
1392 	if (tcsetattr (input, TCSAFLUSH, &term) == -1) {
1393 		errno = ENOTTY;
1394 		close (input);
1395 		return 0;
1396 	}
1397 
1398 	g_assert (write (output, "Enter passphrase: ", sizeof ("Enter passphrase: ") -
1399 		1) != -1);
1400 
1401 	/* Save the current sighandler */
1402 	for (i = 0; i < NSIG; i++) {
1403 		saved_signo[i] = 0;
1404 	}
1405 	sigemptyset (&sa.sa_mask);
1406 	sa.sa_flags = 0;
1407 	sa.sa_handler = read_pass_tmp_sig_handler;
1408 	(void)sigaction (SIGALRM, &sa, &savealrm);
1409 	(void)sigaction (SIGHUP, &sa, &savehup);
1410 	(void)sigaction (SIGINT, &sa, &saveint);
1411 	(void)sigaction (SIGPIPE, &sa, &savepipe);
1412 	(void)sigaction (SIGQUIT, &sa, &savequit);
1413 	(void)sigaction (SIGTERM, &sa, &saveterm);
1414 	(void)sigaction (SIGTSTP, &sa, &savetstp);
1415 	(void)sigaction (SIGTTIN, &sa, &savettin);
1416 	(void)sigaction (SIGTTOU, &sa, &savettou);
1417 
1418 	/* Now read a passphrase */
1419 	p = buf;
1420 	end = p + size - 1;
1421 	while (read (input, &ch, 1) == 1 && ch != '\n' && ch != '\r') {
1422 		if (p < end) {
1423 			*p++ = ch;
1424 		}
1425 	}
1426 	*p = '\0';
1427 	g_assert (write (output, "\n", 1) == 1);
1428 
1429 	/* Restore terminal state */
1430 	if (memcmp (&term, &oterm, sizeof (term)) != 0) {
1431 		while (tcsetattr (input, TCSAFLUSH, &oterm) == -1 &&
1432 			errno == EINTR && !saved_signo[SIGTTOU]) ;
1433 	}
1434 
1435 	/* Restore signal handlers */
1436 	(void)sigaction (SIGALRM, &savealrm, NULL);
1437 	(void)sigaction (SIGHUP, &savehup, NULL);
1438 	(void)sigaction (SIGINT, &saveint, NULL);
1439 	(void)sigaction (SIGQUIT, &savequit, NULL);
1440 	(void)sigaction (SIGPIPE, &savepipe, NULL);
1441 	(void)sigaction (SIGTERM, &saveterm, NULL);
1442 	(void)sigaction (SIGTSTP, &savetstp, NULL);
1443 	(void)sigaction (SIGTTIN, &savettin, NULL);
1444 	(void)sigaction (SIGTTOU, &savettou, NULL);
1445 
1446 	close (input);
1447 
1448 	/* Send signals pending */
1449 	for (i = 0; i < NSIG; i++) {
1450 		if (saved_signo[i]) {
1451 			kill (getpid (), i);
1452 			switch (i) {
1453 			case SIGTSTP:
1454 			case SIGTTIN:
1455 			case SIGTTOU:
1456 				goto restart;
1457 			}
1458 		}
1459 	}
1460 
1461 	return (p - buf);
1462 #endif
1463 }
1464 
1465 #ifdef HAVE_CLOCK_GETTIME
1466 # ifdef CLOCK_MONOTONIC_COARSE
1467 #  define RSPAMD_FAST_MONOTONIC_CLOCK CLOCK_MONOTONIC_COARSE
1468 # elif defined(CLOCK_MONOTONIC_FAST)
1469 #  define RSPAMD_FAST_MONOTONIC_CLOCK CLOCK_MONOTONIC_FAST
1470 # else
1471 #  define RSPAMD_FAST_MONOTONIC_CLOCK CLOCK_MONOTONIC
1472 # endif
1473 #endif
1474 
1475 gdouble
rspamd_get_ticks(gboolean rdtsc_ok)1476 rspamd_get_ticks (gboolean rdtsc_ok)
1477 {
1478 	gdouble res;
1479 
1480 #ifdef HAVE_RDTSC
1481 # ifdef __x86_64__
1482 	guint64 r64;
1483 
1484 	if (rdtsc_ok) {
1485 		__builtin_ia32_lfence ();
1486 		r64 = __rdtsc ();
1487 		/* Preserve lower 52 bits */
1488 		res = r64 & ((1ULL << 53) - 1);
1489 		return res;
1490 	}
1491 # endif
1492 #endif
1493 #ifdef HAVE_CLOCK_GETTIME
1494 	struct timespec ts;
1495 	gint clk_id = RSPAMD_FAST_MONOTONIC_CLOCK;
1496 
1497 	clock_gettime (clk_id, &ts);
1498 
1499 	if (rdtsc_ok) {
1500 		res = (double) ts.tv_sec * 1e9 + ts.tv_nsec;
1501 	}
1502 	else {
1503 		res = (double) ts.tv_sec + ts.tv_nsec / 1000000000.;
1504 	}
1505 # elif defined(__APPLE__)
1506 	if (rdtsc_ok) {
1507 		res = mach_absolute_time ();
1508 	}
1509 	else {
1510 		res = mach_absolute_time () / 1000000000.;
1511 	}
1512 #else
1513 	struct timeval tv;
1514 
1515 	(void)gettimeofday (&tv, NULL);
1516 	if (rdtsc_ok) {
1517 		res = (double) ts.tv_sec * 1e9 + tv.tv_usec * 1e3;
1518 	}
1519 	else {
1520 		res = (double)tv.tv_sec + tv.tv_usec / 1000000.;
1521 	}
1522 #endif
1523 
1524 	return res;
1525 }
1526 
1527 gdouble
rspamd_get_virtual_ticks(void)1528 rspamd_get_virtual_ticks (void)
1529 {
1530 	gdouble res;
1531 
1532 #ifdef HAVE_CLOCK_GETTIME
1533 	struct timespec ts;
1534 	static clockid_t cid = (clockid_t)-1;
1535 	if (cid == (clockid_t)-1) {
1536 # ifdef HAVE_CLOCK_GETCPUCLOCKID
1537 		if (clock_getcpuclockid (0, &cid) == -1) {
1538 # endif
1539 # ifdef CLOCK_PROCESS_CPUTIME_ID
1540 		cid = CLOCK_PROCESS_CPUTIME_ID;
1541 # elif defined(CLOCK_PROF)
1542 		cid = CLOCK_PROF;
1543 # else
1544 		cid = CLOCK_REALTIME;
1545 # endif
1546 # ifdef HAVE_CLOCK_GETCPUCLOCKID
1547 		}
1548 # endif
1549 	}
1550 
1551 	clock_gettime (cid, &ts);
1552 	res = (double)ts.tv_sec + ts.tv_nsec / 1000000000.;
1553 #elif defined(__APPLE__)
1554 	thread_port_t thread = mach_thread_self ();
1555 
1556 	mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
1557 	thread_basic_info_data_t info;
1558 	if (thread_info (thread, THREAD_BASIC_INFO, (thread_info_t)&info, &count) != KERN_SUCCESS) {
1559 		return -1;
1560 	}
1561 
1562 	res = info.user_time.seconds + info.system_time.seconds;
1563 	res += ((gdouble)(info.user_time.microseconds + info.system_time.microseconds)) / 1e6;
1564 	mach_port_deallocate(mach_task_self(), thread);
1565 #elif defined(HAVE_RUSAGE_SELF)
1566 	struct rusage rusage;
1567 	if (getrusage (RUSAGE_SELF, &rusage) != -1) {
1568 		res = (double) rusage.ru_utime.tv_sec +
1569 			  (double) rusage.ru_utime.tv_usec / 1000000.0;
1570 	}
1571 #else
1572 	res = clock () / (double)CLOCKS_PER_SEC;
1573 #endif
1574 
1575 	return res;
1576 }
1577 
1578 gdouble
rspamd_get_calendar_ticks(void)1579 rspamd_get_calendar_ticks (void)
1580 {
1581 	gdouble res;
1582 #ifdef HAVE_CLOCK_GETTIME
1583 	struct timespec ts;
1584 
1585 	clock_gettime (CLOCK_REALTIME, &ts);
1586 	res = ts_to_double (&ts);
1587 #else
1588 	struct timeval tv;
1589 
1590 	if (gettimeofday (&tv, NULL) == 0) {
1591 		res = tv_to_double (&tv);
1592 	}
1593 	else {
1594 		res = time (NULL);
1595 	}
1596 #endif
1597 
1598 	return res;
1599 }
1600 
1601 void
rspamd_random_hex(guchar * buf,guint64 len)1602 rspamd_random_hex (guchar *buf, guint64 len)
1603 {
1604 	static const gchar hexdigests[16] = "0123456789abcdef";
1605 	gint64 i;
1606 
1607 	g_assert (len > 0);
1608 
1609 	ottery_rand_bytes (buf, ceil (len / 2.0));
1610 
1611 	for (i = (gint64)len - 1; i >= 0; i -= 2) {
1612 		buf[i] = hexdigests[buf[i / 2] & 0xf];
1613 
1614 		if (i > 0) {
1615 			buf[i - 1] = hexdigests[(buf[i / 2] >> 4) & 0xf];
1616 		}
1617 	}
1618 }
1619 
1620 gint
rspamd_shmem_mkstemp(gchar * pattern)1621 rspamd_shmem_mkstemp (gchar *pattern)
1622 {
1623 	gint fd = -1;
1624 	gchar *nbuf, *xpos;
1625 	gsize blen;
1626 
1627 	xpos = strchr (pattern, 'X');
1628 
1629 	if (xpos == NULL) {
1630 		errno = EINVAL;
1631 		return -1;
1632 	}
1633 
1634 	blen = strlen (pattern);
1635 	nbuf = g_malloc (blen + 1);
1636 	rspamd_strlcpy (nbuf, pattern, blen + 1);
1637 	xpos = nbuf + (xpos - pattern);
1638 
1639 	for (;;) {
1640 		rspamd_random_hex (xpos, blen - (xpos - nbuf));
1641 
1642 		fd = shm_open (nbuf, O_RDWR | O_EXCL | O_CREAT, 0600);
1643 
1644 		if (fd != -1) {
1645 			rspamd_strlcpy (pattern, nbuf, blen + 1);
1646 			break;
1647 		}
1648 		else if (errno != EEXIST) {
1649 			g_free (nbuf);
1650 
1651 			return -1;
1652 		}
1653 	}
1654 
1655 	g_free (nbuf);
1656 
1657 	return fd;
1658 }
1659 
1660 void
rspamd_ptr_array_free_hard(gpointer p)1661 rspamd_ptr_array_free_hard (gpointer p)
1662 {
1663 	GPtrArray *ar = (GPtrArray *)p;
1664 
1665 	g_ptr_array_free (ar, TRUE);
1666 }
1667 
1668 void
rspamd_array_free_hard(gpointer p)1669 rspamd_array_free_hard (gpointer p)
1670 {
1671 	GArray *ar = (GArray *)p;
1672 
1673 	g_array_free (ar, TRUE);
1674 }
1675 
1676 void
rspamd_gstring_free_hard(gpointer p)1677 rspamd_gstring_free_hard (gpointer p)
1678 {
1679 	GString *ar = (GString *)p;
1680 
1681 	g_string_free (ar, TRUE);
1682 }
1683 
rspamd_gerror_free_maybe(gpointer p)1684 void rspamd_gerror_free_maybe (gpointer p)
1685 {
1686 	GError **err;
1687 
1688 	if (p) {
1689 		err = (GError **)p;
1690 
1691 		if (*err) {
1692 			g_error_free (*err);
1693 		}
1694 	}
1695 }
1696 
1697 /*
1698  * Openblas creates threads that are not supported by
1699  * jemalloc allocator (aside of being bloody stupid). So this hack
1700  * is intended to set number of threads to one by default.
1701  * FIXME: is it legit to do so in ctor?
1702  */
1703 #ifdef HAVE_OPENBLAS_SET_NUM_THREADS
1704 extern void openblas_set_num_threads(int num_threads);
RSPAMD_CONSTRUCTOR(openblas_thread_fix_ctor)1705 RSPAMD_CONSTRUCTOR (openblas_thread_fix_ctor)
1706 {
1707 	openblas_set_num_threads (1);
1708 }
1709 #endif
1710 #ifdef HAVE_BLI_THREAD_SET_NUM_THREADS
1711 extern void bli_thread_set_num_threads(int num_threads);
RSPAMD_CONSTRUCTOR(blis_thread_fix_ctor)1712 RSPAMD_CONSTRUCTOR (blis_thread_fix_ctor)
1713 {
1714 	bli_thread_set_num_threads (1);
1715 }
1716 #endif
1717 
1718 guint64
rspamd_hash_seed(void)1719 rspamd_hash_seed (void)
1720 {
1721 #if 0
1722 	static guint64 seed;
1723 
1724 	if (seed == 0) {
1725 		seed = ottery_rand_uint64 ();
1726 	}
1727 #endif
1728 
1729 	/* Proved to be random, I promise! */
1730 	/*
1731 	 * TODO: discover if it worth to use random seed on run
1732 	 * with ordinary hash function or we need to switch to
1733 	 * siphash1-3 or other slow cooker function...
1734 	 */
1735 	return 0xabf9727ba290690bULL;
1736 }
1737 
1738 static inline gdouble
rspamd_double_from_int64(guint64 x)1739 rspamd_double_from_int64 (guint64 x)
1740 {
1741 	const union { guint64 i; double d; } u = {
1742 			.i = G_GUINT64_CONSTANT(0x3FF) << 52 | x >> 12
1743 	};
1744 
1745 	return u.d - 1.0;
1746 }
1747 
1748 gdouble
rspamd_random_double(void)1749 rspamd_random_double (void)
1750 {
1751 	guint64 rnd_int;
1752 
1753 	rnd_int = ottery_rand_uint64 ();
1754 
1755 	return rspamd_double_from_int64 (rnd_int);
1756 }
1757 
1758 
1759 static guint64*
xorshifto_seed(void)1760 xorshifto_seed (void)
1761 {
1762 	static guint64 xorshifto_seed[4];
1763 	static bool initialized = false;
1764 
1765 	if (G_UNLIKELY(!initialized)) {
1766 		ottery_rand_bytes((void *)xorshifto_seed, sizeof (xorshifto_seed));
1767 		initialized = true;
1768 	}
1769 
1770 	return xorshifto_seed;
1771 }
1772 
1773 static inline guint64
xoroshiro_rotl(const guint64 x,int k)1774 xoroshiro_rotl (const guint64 x, int k) {
1775 	return (x << k) | (x >> (64 - k));
1776 }
1777 
1778 gdouble
rspamd_random_double_fast(void)1779 rspamd_random_double_fast (void)
1780 {
1781 	return rspamd_random_double_fast_seed (xorshifto_seed());
1782 }
1783 
1784 /* xoshiro256+ */
1785 inline gdouble
rspamd_random_double_fast_seed(guint64 seed[4])1786 rspamd_random_double_fast_seed (guint64 seed[4])
1787 {
1788 	const uint64_t result = seed[0] + seed[3];
1789 
1790 	const uint64_t t = seed[1] << 17;
1791 
1792 	seed[2] ^= seed[0];
1793 	seed[3] ^= seed[1];
1794 	seed[1] ^= seed[2];
1795 	seed[0] ^= seed[3];
1796 
1797 	seed[2] ^= t;
1798 
1799 	seed[3] = xoroshiro_rotl (seed[3], 45);
1800 
1801 	return rspamd_double_from_int64 (result);
1802 }
1803 
1804 /* xoroshiro256** */
1805 static inline guint64
rspamd_random_uint64_fast_seed(guint64 seed[4])1806 rspamd_random_uint64_fast_seed (guint64 seed[4])
1807 {
1808 	const uint64_t result = xoroshiro_rotl (seed[1] * 5, 7) * 9;
1809 
1810 	const uint64_t t = seed[1] << 17;
1811 
1812 	seed[2] ^= seed[0];
1813 	seed[3] ^= seed[1];
1814 	seed[1] ^= seed[2];
1815 	seed[0] ^= seed[3];
1816 
1817 	seed[2] ^= t;
1818 
1819 	seed[3] = xoroshiro_rotl (seed[3], 45);
1820 
1821 	return result;
1822 }
1823 
1824 guint64
rspamd_random_uint64_fast(void)1825 rspamd_random_uint64_fast (void)
1826 {
1827 	return rspamd_random_uint64_fast_seed (xorshifto_seed());
1828 }
1829 
1830 void
rspamd_random_seed_fast(void)1831 rspamd_random_seed_fast (void)
1832 {
1833 	(void)xorshifto_seed();
1834 }
1835 
1836 gdouble
rspamd_time_jitter(gdouble in,gdouble jitter)1837 rspamd_time_jitter (gdouble in, gdouble jitter)
1838 {
1839 	if (jitter == 0) {
1840 		jitter = in;
1841 	}
1842 
1843 	return in + jitter * rspamd_random_double ();
1844 }
1845 
1846 gboolean
rspamd_constant_memcmp(const void * a,const void * b,gsize len)1847 rspamd_constant_memcmp (const void *a, const void *b, gsize len)
1848 {
1849 	gsize lena, lenb, i;
1850 	guint16 d, r = 0, m;
1851 	guint16 v;
1852 	const guint8 *aa = (const guint8 *)a,
1853 			*bb =  (const guint8 *)b;
1854 
1855 	if (len == 0) {
1856 		lena = strlen ((const char*)a);
1857 		lenb = strlen ((const char*)b);
1858 
1859 		if (lena != lenb) {
1860 			return FALSE;
1861 		}
1862 
1863 		len = lena;
1864 	}
1865 
1866 	for (i = 0; i < len; i++) {
1867 		v = ((guint16)(guint8)r) + 255;
1868 		m = v / 256 - 1;
1869 		d = (guint16)((int)aa[i] - (int)bb[i]);
1870 		r |= (d & m);
1871 	}
1872 
1873 	return (((gint32)(guint16)((guint32)r + 0x8000) - 0x8000) == 0);
1874 }
1875 
1876 int
rspamd_file_xopen(const char * fname,int oflags,guint mode,gboolean allow_symlink)1877 rspamd_file_xopen (const char *fname, int oflags, guint mode,
1878 		gboolean allow_symlink)
1879 {
1880 	struct stat sb;
1881 	int fd, flags = oflags;
1882 
1883 	if (lstat (fname, &sb) == -1) {
1884 
1885 		if (errno != ENOENT) {
1886 			return (-1);
1887 		}
1888 	}
1889 	else if (!S_ISREG (sb.st_mode)) {
1890 		if (S_ISLNK (sb.st_mode)) {
1891 			if (!allow_symlink) {
1892 				return -1;
1893 			}
1894 		}
1895 		else {
1896 			return -1;
1897 		}
1898 	}
1899 
1900 #ifdef HAVE_OCLOEXEC
1901 	flags |= O_CLOEXEC;
1902 #endif
1903 
1904 #ifdef HAVE_ONOFOLLOW
1905 	if (!allow_symlink) {
1906 		flags |= O_NOFOLLOW;
1907 		fd = open (fname, flags, mode);
1908 	}
1909 	else {
1910 		fd = open (fname, flags, mode);
1911 	}
1912 #else
1913 	fd = open (fname, flags, mode);
1914 #endif
1915 
1916 #ifndef HAVE_OCLOEXEC
1917 	int serrno;
1918 	if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) {
1919 		serrno = errno;
1920 		close (fd);
1921 		errno = serrno;
1922 
1923 		return -1;
1924 	}
1925 #endif
1926 
1927 	return (fd);
1928 }
1929 
1930 gpointer
rspamd_file_xmap(const char * fname,guint mode,gsize * size,gboolean allow_symlink)1931 rspamd_file_xmap (const char *fname, guint mode, gsize *size,
1932 		gboolean allow_symlink)
1933 {
1934 	gint fd;
1935 	struct stat sb;
1936 	gpointer map;
1937 
1938 	g_assert (fname != NULL);
1939 	g_assert (size != NULL);
1940 
1941 	if (mode & PROT_WRITE) {
1942 		fd = rspamd_file_xopen (fname, O_RDWR, 0, allow_symlink);
1943 	}
1944 	else {
1945 		fd = rspamd_file_xopen (fname, O_RDONLY, 0, allow_symlink);
1946 	}
1947 
1948 	if (fd == -1) {
1949 		return NULL;
1950 	}
1951 
1952 	if (fstat (fd, &sb) == -1 || !S_ISREG (sb.st_mode)) {
1953 		close (fd);
1954 		*size = (gsize)-1;
1955 
1956 		return NULL;
1957 	}
1958 
1959 	if (sb.st_size == 0) {
1960 		close (fd);
1961 		*size = (gsize)0;
1962 
1963 		return NULL;
1964 	}
1965 
1966 	map = mmap (NULL, sb.st_size, mode, MAP_SHARED, fd, 0);
1967 	close (fd);
1968 
1969 	if (map == MAP_FAILED) {
1970 		return NULL;
1971 	}
1972 
1973 	*size = sb.st_size;
1974 
1975 	return map;
1976 }
1977 
1978 
1979 gpointer
rspamd_shmem_xmap(const char * fname,guint mode,gsize * size)1980 rspamd_shmem_xmap (const char *fname, guint mode,
1981 		gsize *size)
1982 {
1983 	gint fd;
1984 	struct stat sb;
1985 	gpointer map;
1986 
1987 	g_assert (fname != NULL);
1988 	g_assert (size != NULL);
1989 
1990 #ifdef HAVE_SANE_SHMEM
1991 	if (mode & PROT_WRITE) {
1992 		fd = shm_open (fname, O_RDWR, 0);
1993 	}
1994 	else {
1995 		fd = shm_open (fname, O_RDONLY, 0);
1996 	}
1997 #else
1998 	if (mode & PROT_WRITE) {
1999 		fd = open (fname, O_RDWR, 0);
2000 	}
2001 	else {
2002 		fd = open (fname, O_RDONLY, 0);
2003 	}
2004 #endif
2005 
2006 	if (fd == -1) {
2007 		return NULL;
2008 	}
2009 
2010 	if (fstat (fd, &sb) == -1) {
2011 		close (fd);
2012 
2013 		return NULL;
2014 	}
2015 
2016 	map = mmap (NULL, sb.st_size, mode, MAP_SHARED, fd, 0);
2017 	close (fd);
2018 
2019 	if (map == MAP_FAILED) {
2020 		return NULL;
2021 	}
2022 
2023 	*size = sb.st_size;
2024 
2025 	return map;
2026 }
2027 
2028 /*
2029  * A(x - 0.5)^4 + B(x - 0.5)^3 + C(x - 0.5)^2 + D(x - 0.5)
2030  * A = 32,
2031  * B = -6
2032  * C = -7
2033  * D = 3
2034  * y = 32(x - 0.5)^4 - 6(x - 0.5)^3 - 7(x - 0.5)^2 + 3(x - 0.5)
2035  *
2036  * New approach:
2037  * y = ((x - bias)*2)^8
2038  */
2039 gdouble
rspamd_normalize_probability(gdouble x,gdouble bias)2040 rspamd_normalize_probability (gdouble x, gdouble bias)
2041 {
2042 	gdouble xx;
2043 
2044 	xx = (x - bias) * 2.0;
2045 
2046 	return pow (xx, 8);
2047 }
2048 
2049 /*
2050  * Calculations from musl libc
2051  */
2052 guint64
rspamd_tm_to_time(const struct tm * tm,glong tz)2053 rspamd_tm_to_time (const struct tm *tm, glong tz)
2054 {
2055 	guint64 result;
2056 	gboolean is_leap = FALSE;
2057 	gint leaps, y = tm->tm_year, cycles, rem, centuries;
2058 	glong offset = (tz / 100) * 3600 + (tz % 100) * 60;
2059 
2060 	/* How many seconds in each month from the beginning of the year */
2061 	static const gint secs_through_month[] = {
2062 			0, 31*86400, 59*86400, 90*86400,
2063 			120*86400, 151*86400, 181*86400, 212*86400,
2064 			243*86400, 273*86400, 304*86400, 334*86400
2065 	};
2066 
2067 	/* Convert year */
2068 	if (tm->tm_year - 2ULL <= 136) {
2069 		leaps = (y - 68) / 4;
2070 
2071 		if (!((y - 68) & 3)) {
2072 			leaps--;
2073 			is_leap = 1;
2074 		}
2075 
2076 		result = 31536000 * (y - 70) + 86400 * leaps;
2077 	}
2078 	else {
2079 		cycles = (y - 100) / 400;
2080 		rem = (y - 100) % 400;
2081 		if (rem < 0) {
2082 			cycles--;
2083 			rem += 400;
2084 		}
2085 
2086 		if (!rem) {
2087 			is_leap = 1;
2088 			centuries = 0;
2089 			leaps = 0;
2090 		}
2091 		else {
2092 			if (rem >= 200) {
2093 				if (rem >= 300) {
2094 					centuries = 3;
2095 					rem -= 300;
2096 				}
2097 				else {
2098 					centuries = 2;
2099 					rem -= 200;
2100 				}
2101 			}
2102 			else {
2103 				if (rem >= 100) {
2104 					centuries = 1;
2105 					rem -= 100;
2106 				}
2107 				else {
2108 					centuries = 0;
2109 				}
2110 			}
2111 
2112 			if (!rem) {
2113 				is_leap = 1;
2114 				leaps = 0;
2115 			} else {
2116 				leaps = rem / 4U;
2117 				rem %= 4U;
2118 				is_leap = !rem;
2119 			}
2120 		}
2121 
2122 		leaps += 97 * cycles + 24 * centuries - (gint)is_leap;
2123 		result = (y - 100) * 31536000LL + leaps * 86400LL + 946684800 + 86400;
2124 	}
2125 
2126 	/* Now convert months to seconds */
2127 	result += secs_through_month[tm->tm_mon];
2128 	/* One more day */
2129 	if (is_leap && tm->tm_mon >= 2) {
2130 		result += 86400;
2131 	}
2132 
2133 	result += 86400LL * (tm->tm_mday-1);
2134 	result += 3600LL * tm->tm_hour;
2135 	result += 60LL * tm->tm_min;
2136 	result += tm->tm_sec;
2137 
2138 	/* Now apply tz offset */
2139 	result -= offset;
2140 
2141 	return result;
2142 }
2143 
2144 
2145 void
rspamd_gmtime(gint64 ts,struct tm * dest)2146 rspamd_gmtime (gint64 ts, struct tm *dest)
2147 {
2148 	guint64 days, secs, years;
2149 	int remdays, remsecs, remyears;
2150 	int leap_400_cycles, leap_100_cycles, leap_4_cycles;
2151 	int months;
2152 	int wday, yday, leap;
2153 	/* From March */
2154 	static const uint8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29};
2155 	static const guint64 leap_epoch = 946684800ULL + 86400 * (31 + 29);
2156 	static const guint64 days_per_400y = 365*400 + 97;
2157 	static const guint64 days_per_100y = 365*100 + 24;
2158 	static const guint64 days_per_4y = 365*4 + 1;
2159 
2160 	secs = ts - leap_epoch;
2161 	days = secs / 86400;
2162 	remsecs = secs % 86400;
2163 
2164 	if (remsecs < 0) {
2165 		remsecs += 86400;
2166 		days--;
2167 	}
2168 
2169 	wday = (3 + days) % 7;
2170 	if (wday < 0) {
2171 		wday += 7;
2172 	}
2173 
2174 	/* Deal with gregorian adjustments */
2175 	leap_400_cycles = days / days_per_400y;
2176 	remdays = days % days_per_400y;
2177 
2178 	if (remdays < 0) {
2179 		remdays += days_per_400y;
2180 		leap_400_cycles--;
2181 	}
2182 
2183 	leap_100_cycles = remdays / days_per_100y;
2184 	if (leap_100_cycles == 4) {
2185 		/* 400 years */
2186 		leap_100_cycles--;
2187 	}
2188 
2189 	remdays -= leap_100_cycles * days_per_100y;
2190 
2191 	leap_4_cycles = remdays / days_per_4y;
2192 	if (leap_4_cycles == 25) {
2193 		/* 100 years */
2194 		leap_4_cycles--;
2195 	}
2196 	remdays -= leap_4_cycles * days_per_4y;
2197 
2198 	remyears = remdays / 365;
2199 	if (remyears == 4) {
2200 		/* Ordinary leap year */
2201 		remyears--;
2202 	}
2203 	remdays -= remyears * 365;
2204 
2205 	leap = !remyears && (leap_4_cycles || !leap_100_cycles);
2206 	yday = remdays + 31 + 28 + leap;
2207 
2208 	if (yday >= 365 + leap) {
2209 		yday -= 365 + leap;
2210 	}
2211 
2212 	years = remyears + 4 * leap_4_cycles + 100 * leap_100_cycles +
2213 			400ULL * leap_400_cycles;
2214 
2215 	for (months=0; days_in_month[months] <= remdays; months++) {
2216 		remdays -= days_in_month[months];
2217 	}
2218 
2219 	if (months >= 10) {
2220 		months -= 12;
2221 		years++;
2222 	}
2223 
2224 	dest->tm_year = years + 100;
2225 	dest->tm_mon = months + 2;
2226 	dest->tm_mday = remdays + 1;
2227 	dest->tm_wday = wday;
2228 	dest->tm_yday = yday;
2229 
2230 	dest->tm_hour = remsecs / 3600;
2231 	dest->tm_min = remsecs / 60 % 60;
2232 	dest->tm_sec = remsecs % 60;
2233 #if !defined(__sun)
2234 	dest->tm_gmtoff = 0;
2235 	dest->tm_zone = "GMT";
2236 #endif
2237 }
2238 
2239 void
rspamd_localtime(gint64 ts,struct tm * dest)2240 rspamd_localtime (gint64 ts, struct tm *dest)
2241 {
2242 	time_t t = ts;
2243 	localtime_r (&t, dest);
2244 }
2245 
2246 gboolean
rspamd_fstring_gzip(rspamd_fstring_t ** in)2247 rspamd_fstring_gzip (rspamd_fstring_t **in)
2248 {
2249 	z_stream strm;
2250 	gint rc;
2251 	rspamd_fstring_t *comp, *buf = *in;
2252 	gchar *p;
2253 	gsize remain;
2254 
2255 	memset (&strm, 0, sizeof (strm));
2256 	rc = deflateInit2 (&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
2257 			MAX_WBITS + 16, MAX_MEM_LEVEL - 1, Z_DEFAULT_STRATEGY);
2258 
2259 	if (rc != Z_OK) {
2260 		return FALSE;
2261 	}
2262 
2263 	comp = rspamd_fstring_sized_new (deflateBound (&strm, buf->len));
2264 
2265 	strm.avail_in = buf->len;
2266 	strm.next_in = (guchar *)buf->str;
2267 	p = comp->str;
2268 	remain = comp->allocated;
2269 
2270 	while (strm.avail_in != 0) {
2271 		strm.avail_out = remain;
2272 		strm.next_out = p;
2273 
2274 		rc = deflate (&strm, Z_FINISH);
2275 
2276 		if (rc != Z_OK && rc != Z_BUF_ERROR) {
2277 			if (rc == Z_STREAM_END) {
2278 				break;
2279 			}
2280 			else {
2281 				rspamd_fstring_free (comp);
2282 				deflateEnd (&strm);
2283 
2284 				return FALSE;
2285 			}
2286 		}
2287 
2288 		comp->len = strm.total_out;
2289 
2290 		if (strm.avail_out == 0 && strm.avail_in != 0) {
2291 			/* Need to allocate more */
2292 			remain = comp->len;
2293 			comp = rspamd_fstring_grow (comp, strm.avail_in);
2294 			p = comp->str + remain;
2295 			remain = comp->allocated - remain;
2296 		}
2297 	}
2298 
2299 	deflateEnd (&strm);
2300 	comp->len = strm.total_out;
2301 	rspamd_fstring_free (buf); /* We replace buf with its compressed version */
2302 	*in = comp;
2303 
2304 	return TRUE;
2305 }
2306 
2307 static gboolean
rspamd_glob_dir(const gchar * full_path,const gchar * pattern,gboolean recursive,guint rec_len,GPtrArray * res,GError ** err)2308 rspamd_glob_dir (const gchar *full_path, const gchar *pattern,
2309 				 gboolean recursive, guint rec_len,
2310 				 GPtrArray *res, GError **err)
2311 {
2312 	glob_t globbuf;
2313 	const gchar *path;
2314 	static gchar pathbuf[PATH_MAX]; /* Static to help recursion */
2315 	guint i;
2316 	gint rc;
2317 	static const guint rec_lim = 16;
2318 	struct stat st;
2319 
2320 	if (rec_len > rec_lim) {
2321 		g_set_error (err, g_quark_from_static_string ("glob"), EOVERFLOW,
2322 				"maximum nesting is reached: %d", rec_lim);
2323 
2324 		return FALSE;
2325 	}
2326 
2327 	memset (&globbuf, 0, sizeof (globbuf));
2328 
2329 	if ((rc = glob (full_path, 0, NULL, &globbuf)) != 0) {
2330 
2331 		if (rc != GLOB_NOMATCH) {
2332 			g_set_error (err, g_quark_from_static_string ("glob"), errno,
2333 					"glob %s failed: %s", full_path, strerror (errno));
2334 			globfree (&globbuf);
2335 
2336 			return FALSE;
2337 		}
2338 		else {
2339 			globfree (&globbuf);
2340 
2341 			return TRUE;
2342 		}
2343 	}
2344 
2345 	for (i = 0; i < globbuf.gl_pathc; i ++) {
2346 		path = globbuf.gl_pathv[i];
2347 
2348 		if (stat (path, &st) == -1) {
2349 			if (errno == EPERM || errno == EACCES || errno == ELOOP) {
2350 				/* Silently ignore */
2351 				continue;
2352 			}
2353 
2354 			g_set_error (err, g_quark_from_static_string ("glob"), errno,
2355 					"stat %s failed: %s", path, strerror (errno));
2356 			globfree (&globbuf);
2357 
2358 			return FALSE;
2359 		}
2360 
2361 		if (S_ISREG (st.st_mode)) {
2362 			g_ptr_array_add (res, g_strdup (path));
2363 		}
2364 		else if (recursive && S_ISDIR (st.st_mode)) {
2365 			rspamd_snprintf (pathbuf, sizeof (pathbuf), "%s%c%s",
2366 					path, G_DIR_SEPARATOR, pattern);
2367 
2368 			if (!rspamd_glob_dir (full_path, pattern, recursive, rec_len + 1,
2369 					res, err)) {
2370 				globfree (&globbuf);
2371 
2372 				return FALSE;
2373 			}
2374 		}
2375 	}
2376 
2377 	globfree (&globbuf);
2378 
2379 	return TRUE;
2380 }
2381 
2382 GPtrArray *
rspamd_glob_path(const gchar * dir,const gchar * pattern,gboolean recursive,GError ** err)2383 rspamd_glob_path (const gchar *dir,
2384 				  const gchar *pattern,
2385 				  gboolean recursive,
2386 				  GError **err)
2387 {
2388 	gchar path[PATH_MAX];
2389 	GPtrArray *res;
2390 
2391 	res = g_ptr_array_new_full (32, (GDestroyNotify)g_free);
2392 	rspamd_snprintf (path, sizeof (path), "%s%c%s", dir, G_DIR_SEPARATOR, pattern);
2393 
2394 	if (!rspamd_glob_dir (path, pattern, recursive, 0, res, err)) {
2395 		g_ptr_array_free (res, TRUE);
2396 
2397 		return NULL;
2398 	}
2399 
2400 	return res;
2401 }
2402 
2403 double
rspamd_set_counter(struct rspamd_counter_data * cd,gdouble value)2404 rspamd_set_counter (struct rspamd_counter_data *cd, gdouble value)
2405 {
2406 	gdouble cerr;
2407 
2408 	/* Cumulative moving average using per-process counter data */
2409 	if (cd->number == 0) {
2410 		cd->mean = 0;
2411 		cd->stddev = 0;
2412 	}
2413 
2414 	cd->mean += (value - cd->mean) / (gdouble)(++cd->number);
2415 	cerr = (value - cd->mean) * (value - cd->mean);
2416 	cd->stddev += (cerr - cd->stddev) / (gdouble)(cd->number);
2417 
2418 	return cd->mean;
2419 }
2420 
2421 float
rspamd_set_counter_ema(struct rspamd_counter_data * cd,float value,float alpha)2422 rspamd_set_counter_ema (struct rspamd_counter_data *cd,
2423 		float value,
2424 		float alpha)
2425 {
2426 	float diff, incr;
2427 
2428 	/* Cumulative moving average using per-process counter data */
2429 	if (cd->number == 0) {
2430 		cd->mean = 0;
2431 		cd->stddev = 0;
2432 	}
2433 
2434 	diff = value - cd->mean;
2435 	incr = diff * alpha;
2436 	cd->mean += incr;
2437 	cd->stddev = (1.0f - alpha) * (cd->stddev + diff * incr);
2438 	cd->number ++;
2439 
2440 	return cd->mean;
2441 }
2442 
2443 void
rspamd_ptr_array_shuffle(GPtrArray * ar)2444 rspamd_ptr_array_shuffle (GPtrArray *ar)
2445 {
2446 	if (ar->len < 2) {
2447 		return;
2448 	}
2449 
2450 	guint n = ar->len;
2451 
2452 	for (guint i = 0; i < n - 1; i++) {
2453 		guint j = i + rspamd_random_uint64_fast () % (n - i);
2454 		gpointer t = g_ptr_array_index (ar, j);
2455 		g_ptr_array_index (ar, j) = g_ptr_array_index (ar, i);
2456 		g_ptr_array_index (ar, i) = t;
2457 	}
2458 }
2459