1 /*-
2  * Copyright (c) 2013-2015, Alexey Savelyev <info@homeweb.ru>
3  * Copyright 2017 Vsevolod Stakhov
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 
15  * Based on source code from http://www.ols.es/exim/dlext/ by David Saez <david@ols.es>,
16  * source code of exim by Philip Hazel <ph10@cam.ac.uk>
17  * and source code of exiscan by Tom Kistner <tom@duncanthrax.net>
18  * and source code of Victor Ustugov http://mta.org.ua/
19 */
20 
21 #include "exim.h"
22 
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/wait.h>
28 #include <stdarg.h>
29 #include <errno.h>
30 #include "cJSON.h"
31 #include "cJSON.c"
32 
33 #define RSPAMD_TIMEOUT        120
34 
35 extern uschar *tod_stamp (int);
36 //extern BOOL    split_spool_directory;  /* TRUE to use multiple subdirs */
37 //extern uschar *spool_directory;        /* Name of spool directory */
38 //extern uschar  message_subdir[];       /* Subdirectory for messages */
39 
40 //-------------------------------------------------------------------------
41 
42 int
rspamd(uschar ** yield,int argc,uschar * argv[])43 rspamd (uschar **yield, int argc, uschar *argv[]) {
44 	char *arg_socket_addr;
45 	char *arg_defer_ok;
46 	int defer_ok;
47 	int rspamd_command;
48 	char tcp_addr[15];
49 	int tcp_port;
50 	FILE *mbox_file = NULL;
51 //    unsigned long mbox_size;
52 	off_t mbox_size;
53 	uschar *s, *p;
54 	header_line *my_header, *header_new, *header_last, *tmp_headerlist;
55 	header_line *last_received = NULL;
56 	uschar *address;
57 	uschar *helo;
58 	uschar *sender_host_name;
59 	uschar *authenticated_id;
60 	char mbox_path[8192];
61 	int max_len, len;
62 	int rspamd_sock = 0;
63 	struct hostent *he;
64 	struct in_addr in;
65 	struct sockaddr_un server;
66 #ifndef NO_POLL_H
67 	int result;
68 	struct pollfd pollfd;
69 #endif
70 	int offset;
71 	uschar spamd_buffer[32600];
72 	uschar spamd_buffer2[32600];
73 	time_t start;
74 	size_t read, wrote;
75 	int i, j, c;
76 
77 	arg_socket_addr = argv[0];
78 	arg_defer_ok = argv[1];
79 
80 	if (argc < 2) {
81 		defer_ok = 0;
82 	} else if (strcmpic (arg_defer_ok, US"1") == 0)
83 	|| (strcmpic (arg_defer_ok, US
84 	"yes") == 0)
85 	|| (strcmpic (arg_defer_ok, US
86 	"true") == 0)
87 	|| (strcmpic (arg_defer_ok, US
88 	"defer_ok") == 0) {
89 		defer_ok = 1;
90 	} else {
91 		defer_ok = 0;
92 	}
93 
94 	if ((arg_socket_addr == NULL) || (arg_socket_addr[0] == 0)) {
95 		log_write (0, LOG_MAIN | LOG_PANIC,
96 				"rspamd dlfunc: Socket address expected");
97 		*yield = string_sprintf ("rspamd dlfunc: Socket address expected");
98 		goto RETURN_DEFER;
99 	}
100 
101 
102 	if (split_spool_directory == 0) {
103 		snprintf (mbox_path, sizeof (mbox_path), "%s/input/%s-D",
104 				spool_directory,
105 				message_id);
106 	} else {
107 		snprintf (mbox_path, sizeof (mbox_path), "%s/input/%s/%s-D",
108 				spool_directory, message_subdir, message_id);
109 	}
110 
111 	mbox_file = fopen (mbox_path, "rb");
112 
113 	if (!mbox_file) {
114 		*yield = string_sprintf ("rspamd dlfunc: Unable to spool message '%s'",
115 				mbox_path);
116 		return (defer_ok ? OK : ERROR);
117 	}
118 
119 	(void) fseek (mbox_file, 0, SEEK_END);
120 	mbox_size = ftell (mbox_file);
121 //debug_printf("  Total spool file size: %d\n", mbox_size);
122 	mbox_size -= SPOOL_DATA_START_OFFSET;
123 //debug_printf("  Spool file size: %d\n", mbox_size);
124 //debug_printf("  fseek %d, %d\n", SPOOL_DATA_START_OFFSET, SEEK_SET);
125 	(void) fseek (mbox_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
126 
127 	start = time (NULL);
128 	/* socket does not start with '/' -> network socket */
129 	if (arg_socket_addr[0] != '/') {
130 		if (sscanf (CS arg_socket_addr, "%s %u", tcp_addr, &tcp_port) != 2 ) {
131 			log_write (0, LOG_MAIN | LOG_PANIC,
132 					"rspamd dlfunc: Invalid rspamd address: '%s'",
133 					arg_socket_addr);
134 			*yield = string_sprintf (
135 					"rspamd dlfunc: Invalid rspamd address: '%s'",
136 					arg_socket_addr);
137 			goto RETURN_DEFER;
138 		}
139 
140 		/* Lookup the host */
141 		if ((he = gethostbyname (CS tcp_addr)) == 0) {
142 			log_write (0, LOG_MAIN | LOG_PANIC,
143 					"rspamd dlfunc: failed to lookup host '%s'", tcp_addr);
144 			*yield = string_sprintf (
145 					"rspamd dlfunc: failed to lookup host '%s'", tcp_addr);
146 			goto RETURN_DEFER;
147 		}
148 
149 		in = *(struct in_addr *) he->h_addr_list[0];
150 
151 /* contact a rspamd */
152 
153 		if ((rspamd_sock = ip_socket (SOCK_STREAM, AF_INET)) < 0) {
154 			log_write (0, LOG_MAIN | LOG_PANIC,
155 					"rspamd dlfunc: TCP socket creation failed: %s",
156 					strerror (errno));
157 			*yield = string_sprintf (
158 					"rspamd dlfunc: TCP socket creation failed: %s",
159 					strerror (errno));
160 			goto RETURN_DEFER;
161 		};
162 
163 		if (ip_connect (rspamd_sock, AF_INET, (uschar *) inet_ntoa (in),
164 				tcp_port, 5, FALSE) < 0) {
165 			log_write (0, LOG_MAIN | LOG_PANIC,
166 					"rspamd dlfunc: connection to %s, port %u failed: %s",
167 					tcp_addr, tcp_port, strerror (errno));
168 			*yield = string_sprintf (
169 					"rspamd dlfunc: connection to %s, port %u failed: %s",
170 					tcp_addr, tcp_port, strerror (errno));
171 			goto RETURN_DEFER;
172 		}
173 
174 //debug_printf("  Use TCP socket %s:%d\n", tcp_addr, tcp_port);
175 	} else {
176 		if ((rspamd_sock = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
177 			log_write (0, LOG_MAIN | LOG_PANIC,
178 					"rspamd dlfunc: Unable to acquire socket (%s)",
179 					strerror (errno));
180 			*yield = string_sprintf (
181 					"rspamd dlfunc: Unable to acquire socket (%s)",
182 					strerror (errno));
183 			goto RETURN_DEFER;
184 		}
185 
186 		server.sun_family = AF_UNIX;
187 		Ustrcpy (server.sun_path, arg_socket_addr);
188 
189 		if (connect (rspamd_sock, (struct sockaddr *) &server,
190 				sizeof (struct sockaddr_un)) < 0) {
191 			log_write (0, LOG_MAIN | LOG_PANIC,
192 					"rspamd dlfunc: Unable to connect to UNIX socket %s (%s)",
193 					socket, strerror (errno));
194 			*yield = string_sprintf (
195 					"rspamd dlfunc: Unable to connect to UNIX socket %s (%s)",
196 					socket, strerror (errno));
197 			goto RETURN_DEFER;
198 		}
199 
200 //debug_printf("  Use UNIX Domain socket %s\n", arg_socket_addr);
201 	}
202 
203 // now we are connected to rspamd on rspamd_sock
204 
205 	memset (spamd_buffer2, 0, sizeof (spamd_buffer2));
206 	offset = 0;
207 
208 	// headers list
209 	tmp_headerlist = NULL;
210 	header_last = NULL;
211 	for (my_header = header_list; my_header; my_header = my_header->next) {
212 		if ((my_header->type != '*') && (my_header->type != htype_old)) {
213 			header_new = store_get (sizeof (header_line));
214 			header_new->text = my_header->text;
215 
216 			header_new->slen = my_header->slen;
217 			header_new->type = my_header->type;
218 			header_new->next = NULL;
219 			//debug_printf("  copy header item: '%s'\n", my_header->text);
220 
221 			max_len = sizeof (spamd_buffer2) - offset - 1;
222 			len = my_header->slen;
223 			if (len > max_len) len = max_len;
224 			Ustrncpy (spamd_buffer2 + offset, my_header->text, len);
225 			offset += len;
226 
227 			if (header_last != NULL) header_last->next = header_new;
228 			header_last = header_new;
229 		}
230 	}
231 
232 	s = string_sprintf ("\n");
233 	max_len = sizeof (spamd_buffer2) - offset - 1;
234 	len = Ustrlen (s);
235 	if (len > max_len) len = max_len;
236 	Ustrncpy (spamd_buffer2 + offset, s, len);
237 	offset += len;
238 
239 //debug_printf("  Headers size: %d\n", offset);
240 	mbox_size += offset;
241 //debug_printf("  Total message size: %d\n", mbox_size);
242 
243 // copy request to buffer
244 	memset (spamd_buffer, 0, sizeof (spamd_buffer));
245 	string_format (spamd_buffer,
246 			sizeof (spamd_buffer),
247 			"POST /checkv2 HTTP/1.0\r\nContent-length: "OFF_T_FMT
248 			"\r\nQueue-Id: %s\r\nFrom: %s\r\n",
249 			mbox_size, message_id, sender_address);
250 
251 	for (i = 0; i < recipients_count; i++) {
252 		string_format (spamd_buffer + Ustrlen (spamd_buffer),
253 				sizeof (spamd_buffer) - Ustrlen (spamd_buffer), "Rcpt: %s\r\n",
254 				recipients_list[i].address);
255 	}
256 
257 	if ((helo = expand_string (US"$sender_helo_name")) != NULL && *helo != '\0') {
258 		string_format (spamd_buffer + Ustrlen (spamd_buffer),
259 				sizeof (spamd_buffer) - Ustrlen (spamd_buffer), "Helo: %s\r\n",
260 				helo);
261 	}
262 
263 	if ((sender_host_name = expand_string (US"$sender_host_name")) != NULL &&
264 			*sender_host_name != '\0') {
265 		string_format (spamd_buffer + Ustrlen (spamd_buffer),
266 				sizeof (spamd_buffer) - Ustrlen (spamd_buffer),
267 				"Hostname: %s\r\n",
268 				sender_host_name);
269 	}
270 	//else
271 	//string_format(spamd_buffer+Ustrlen(spamd_buffer), sizeof(spamd_buffer)-Ustrlen(spamd_buffer), "Hostname: unknown\r\n");
272 
273 	if (sender_host_address != NULL) {
274 		string_format (spamd_buffer + Ustrlen (spamd_buffer),
275 				sizeof (spamd_buffer) - Ustrlen (spamd_buffer), "IP: %s\r\n",
276 				sender_host_address);
277 	}
278 
279 	//authenticated_id
280 	if ((authenticated_id = expand_string (US"$authenticated_id")) != NULL &&
281 			*authenticated_id != '\0') {
282 		string_format (spamd_buffer + Ustrlen (spamd_buffer),
283 				sizeof (spamd_buffer) - Ustrlen (spamd_buffer), "User: %s\r\n",
284 				authenticated_id);
285 	}
286 
287 	string_format (spamd_buffer + Ustrlen (spamd_buffer),
288 			sizeof (spamd_buffer) - Ustrlen (spamd_buffer), "\r\n");
289 
290 	if (send (rspamd_sock, spamd_buffer, Ustrlen (spamd_buffer), 0) < 0) {
291 		log_write (0, LOG_MAIN | LOG_PANIC,
292 				"rspamd dlfunc: rspamd send failed: %s", strerror (errno));
293 		goto RETURN_DEFER;
294 	}
295 
296 	/*
297 	 * now send the data buffer and spool file
298 	 */
299 	Ustrcpy (big_buffer, "sending data block");
300 
301 	wrote = send (rspamd_sock, spamd_buffer2, strlen (spamd_buffer2), 0);
302 	if (wrote == -1) {
303 		goto WRITE_FAILED;
304 	}
305 
306 	/*
307 	 * Note: poll() is not supported in OSX 10.2.
308 	 */
309 
310 #ifndef NO_POLL_H
311 	pollfd.fd = rspamd_sock;
312 	pollfd.events = POLLOUT;
313 #endif
314 //    (void)fcntl(rspamd_sock, F_SETFL, O_NONBLOCK);
315 	do {
316 		read = fread (spamd_buffer, 1, sizeof (spamd_buffer) - 1, mbox_file);
317 
318 		if (read < sizeof (spamd_buffer)) {
319 			spamd_buffer[read] = 0;
320 		}
321 //debug_printf("  Read from spool file: %s", spamd_buffer);
322 		if (read > 0) {
323 			offset = 0;
324 			again:
325 
326 #ifndef NO_POLL_H
327 			result = poll (&pollfd, 1, 1000);
328 			if (result == -1 && errno == EINTR) {
329 				continue;
330 			}
331 			else if (result < 1) {
332 				if (result == -1)
333 					log_write (0, LOG_MAIN | LOG_PANIC,
334 							"rspamd dlfunc: %s on rspamd socket",
335 							strerror (errno));
336 				else {
337 					if (time (NULL) - start < RSPAMD_TIMEOUT)
338 						goto again;
339 					log_write (0, LOG_MAIN | LOG_PANIC,
340 							"rspamd dlfunc: timed out writing rspamd socket");
341 					*yield = string_sprintf (
342 							"rspamd dlfunc: timed out writing rspamd socket");
343 				}
344 				goto RETURN_DEFER;
345 			}
346 #endif
347 			wrote = send (rspamd_sock, spamd_buffer + offset, read - offset, 0);
348 
349 			if (wrote == -1) {
350 				goto WRITE_FAILED;
351 			}
352 
353 			if (offset + wrote != read) {
354 				offset += wrote;
355 				goto again;
356 			}
357 		}
358 	} while (!feof (mbox_file) && !ferror (mbox_file));
359 
360 	if (ferror (mbox_file)) {
361 		log_write (0, LOG_MAIN | LOG_PANIC,
362 				"rspamd dlfunc: error reading spool file: %s",
363 				strerror (errno));
364 		*yield = string_sprintf ("rspamd dlfunc: error reading spool file: %s",
365 				strerror (errno));
366 		goto RETURN_DEFER;
367 	}
368 
369 	/*
370 	 read rspamd response using what's left of the timeout.
371 	 */
372 
373 	memset (spamd_buffer, 0, sizeof (spamd_buffer));
374 	offset = 0;
375 	while ((i = ip_recv (rspamd_sock,
376 			spamd_buffer + offset,
377 			sizeof (spamd_buffer) - offset - 1,
378 			RSPAMD_TIMEOUT - time (NULL) + start)) > 0
379 			) {
380 		//debug_printf("  read %d bytes from socket\n", i);
381 		offset += i;
382 	}
383 //debug_printf("  total read %d bytes from socket\n", offset);
384 
385 /* error handling */
386 	if ((i <= 0) && (errno != 0)) {
387 		log_write (0, LOG_MAIN | LOG_PANIC,
388 				"rspamd dlfunc: error reading from rspamd socket: %s",
389 				strerror (errno));
390 		*yield = string_sprintf (
391 				"rspamd dlfunc: error reading from rspamd socket: %s",
392 				strerror (errno));
393 		goto RETURN_DEFER;
394 	}
395 
396 //debug_printf("read from socket: %s\n", spamd_buffer);
397 
398 	if (rspamd_sock > 0) {
399 		(void) close (rspamd_sock);
400 		rspamd_sock = 0;
401 	}
402 	if (mbox_file != NULL) {
403 		(void) fclose (mbox_file);
404 		mbox_file = NULL;
405 	}
406 
407 	//Parse http response code
408 	if (strstr (spamd_buffer, "HTTP/1.1 200 OK") == NULL &&
409 			strstr (spamd_buffer, "HTTP/1.0 200 OK") == NULL) {
410 		*yield = string_sprintf ("rspamd dlfunc: HTTP return code != 200: %s",
411 				spamd_buffer);
412 		goto RETURN_DEFER;
413 	}
414 
415 	//Parse http response
416 	const char *crlf_pos = strstr (spamd_buffer, "\r\n\r\n");
417 	if (crlf_pos == NULL) {
418 		*yield = string_sprintf ("rspamd dlfunc: HTTP response error: %s",
419 				spamd_buffer);
420 		goto RETURN_DEFER;
421 	}
422 
423 	char *json_answer = string_sprintf ("%s", crlf_pos + 4);
424 
425 	//Parse json
426 	cJSON *json = NULL;
427 	json = cJSON_Parse (json_answer);
428 
429 	if (!json) {
430 		*yield = string_sprintf ("rspamd dlfunc: Json parse error, json: %s",
431 				spamd_buffer);
432 		goto RETURN_DEFER;
433 	}
434 
435 	//Score
436 	cJSON *score = cJSON_GetObjectItem (json, "score");
437 	if (!cJSON_IsNumber (score)) {
438 		*yield = string_sprintf (
439 				"rspamd dlfunc: Json parse error, no found 'score'");
440 		goto RETURN_DEFER;
441 	}
442 	//required_score
443 	cJSON *required_score = cJSON_GetObjectItem (json, "required_score");
444 	if (!cJSON_IsNumber (required_score)) {
445 		*yield = string_sprintf (
446 				"rspamd dlfunc: Json parse error, no found 'required_score'");
447 		goto RETURN_DEFER;
448 	}
449 	//Action
450 	cJSON *action = cJSON_GetObjectItem (json, "action");
451 	if (!cJSON_IsString (action)) {
452 		*yield = string_sprintf (
453 				"rspamd dlfunc: Json parse error, no found 'action'");
454 		goto RETURN_DEFER;
455 	}
456 	*yield = string_sprintf ("[%.2f / %.2f]", score->valuedouble,
457 			required_score->valuedouble);
458 
459 	//Parse scan time
460 	cJSON *time_real = cJSON_GetObjectItem (json, "time_real");
461 	cJSON *time_virtual = cJSON_GetObjectItem (json, "time_virtual");
462 	if (cJSON_IsNumber (time_real) && cJSON_IsNumber (time_virtual))
463 		*yield = string_sprintf ("%s  [time: %.6f, %.6f]", *yield,
464 				time_real->valuedouble, time_virtual->valuedouble);
465 
466 	*yield = string_sprintf ("%s\n Action: %s\n", *yield, action->valuestring);
467 
468 	cJSON *symbol = NULL;
469 	cJSON *symbol_name = NULL;
470 	cJSON *symbol_score = NULL;
471 	cJSON *symbol_options = NULL;
472 	cJSON *option = NULL;
473 
474 	//parse symbols
475 	cJSON *symbols = cJSON_GetObjectItem (json, "symbols");
476 	for (i = 0; i < cJSON_GetArraySize (symbols); i++) {
477 		symbol = cJSON_GetArrayItem (symbols, i);
478 		symbol_name = cJSON_GetObjectItem (symbol, "name");
479 		symbol_score = cJSON_GetObjectItem (symbol, "score");
480 		symbol_options = cJSON_GetObjectItem (symbol, "options");
481 
482 		if (cJSON_IsString (symbol_name)) {
483 			*yield = string_sprintf ("%s %s", *yield, symbol_name->valuestring);
484 		}
485 		if (cJSON_IsNumber (symbol_score)) {
486 			*yield = string_sprintf ("%s(%.2f)", *yield,
487 					symbol_score->valuedouble);
488 		}
489 
490 		//parse options
491 		c = cJSON_GetArraySize (symbol_options);
492 		if (c > 0) {
493 			*yield = string_sprintf ("%s[", *yield);
494 		}
495 
496 		for (j = 0; j < c; j++) {
497 			option = cJSON_GetArrayItem (symbol_options, j);
498 
499 			if (cJSON_IsString (option)) {
500 				*yield = string_sprintf ("%s%s", *yield, option->valuestring);
501 				if (j < c - 1) *yield = string_sprintf ("%s, ", *yield);
502 			}
503 		}
504 		if (c > 0) {
505 			*yield = string_sprintf ("%s]", *yield);
506 		}
507 
508 		*yield = string_sprintf ("%s\n", *yield);
509 	}
510 
511 	//Parse messages
512 	cJSON *mess = NULL;
513 	cJSON *messages = cJSON_GetObjectItem (json, "messages");
514 	c = cJSON_GetArraySize (messages);
515 
516 	for (i = 0; i < c; i++) {
517 		mess = cJSON_GetArrayItem (messages, i);
518 		if (cJSON_IsString (mess)) {
519 			*yield = string_sprintf ("%s %s", *yield, mess->valuestring);
520 		}
521 
522 		if (i < c - 1) {
523 			*yield = string_sprintf ("%s\n", *yield);
524 		}
525 	}
526 
527 	return OK;
528 
529 /* Come here if any call to read_response, other than a response after the data
530 phase, failed. Analyse the error, and if isn't too bad, send a QUIT
531 command. Wait for the response with a short timeout, so we don't wind up this
532 process before the far end has had time to read the QUIT. */
533 
534 	WRITE_FAILED:
535 	{
536 		log_write (0, LOG_MAIN | LOG_PANIC,
537 				"rspamd dlfunc: %s on rspamd socket", strerror (errno));
538 		*yield = string_sprintf ("rspamd dlfunc: %s on rspamd socket",
539 				strerror (errno));
540 		goto RETURN_DEFER;
541 	}
542 
543 	RESPONSE_FAILED:
544 	{
545 		int code;
546 		int save_errno;
547 		int more_errno;
548 		uschar message_buffer[256];
549 		uschar *message;
550 
551 		save_errno = errno;
552 
553 		message = &message_buffer[0];
554 
555 		log_write (0, LOG_MAIN | LOG_PANIC, "rspamd dlfunc: %s", message);
556 		*yield = string_sprintf ("rspamd dlfunc: %s", message);
557 
558 		goto RETURN_DEFER;
559 	}
560 
561 	RETURN_DEFER:
562 	{
563 		if (rspamd_sock > 0) {
564 			(void) close (rspamd_sock);
565 			rspamd_sock = 0;
566 		}
567 		if (mbox_file != NULL) {
568 			(void) fclose (mbox_file);
569 			mbox_file = NULL;
570 		}
571 
572 		return (defer_ok ? OK : ERROR);
573 	}
574 
575 	return OK;
576 }
577