Browse code

mbox optimisation to reduce the lifetime of temporary files

git-svn: trunk@3193

Nigel Horne authored on 2007/08/30 03:21:39
Showing 4 changed files
... ...
@@ -1,3 +1,7 @@
1
+Wed Aug 29 18:27:55 BST 2007 (njh)
2
+----------------------------------
3
+  * libclamav:	mbox optimisation to reduce the lifetime of temporary files
4
+
1 5
 Tue Aug 28 16:08:13 BST 2007 (njh)
2 6
 ----------------------------------
3 7
   * libclamav/mbox.c:	MailFollowURLS: improved debugging
... ...
@@ -48,6 +48,7 @@ static	char	const	rcsid[] = "$Id: blob.c,v 1.64 2007/02/12 22:25:14 njh Exp $";
48 48
 #include "others.h"
49 49
 #include "mbox.h"
50 50
 #include "matcher.h"
51
+#include "scanners.h"
51 52
 
52 53
 #ifndef	CL_DEBUG
53 54
 #define	NDEBUG	/* map CLAMAV debug onto standard */
... ...
@@ -214,7 +215,7 @@ blobAddData(blob *b, const unsigned char *data, size_t len)
214 214
 		assert(b->len == 0);
215 215
 		assert(b->size == 0);
216 216
 
217
-		b->size = len * 4;
217
+		b->size = (off_t)len * 4;
218 218
 		b->data = cli_malloc(b->size);
219 219
 	} else if(b->size < b->len + (off_t)len) {
220 220
 		unsigned char *p = cli_realloc(b->data, b->size + (len * 4));
... ...
@@ -222,14 +223,14 @@ blobAddData(blob *b, const unsigned char *data, size_t len)
222 222
 		if(p == NULL)
223 223
 			return -1;
224 224
 
225
-		b->size += len * 4;
225
+		b->size += (off_t)len * 4;
226 226
 		b->data = p;
227 227
 	}
228 228
 #endif
229 229
 
230 230
 	if(b->data) {
231 231
 		memcpy(&b->data[b->len], data, len);
232
-		b->len += len;
232
+		b->len += (off_t)len;
233 233
 	}
234 234
 	return 0;
235 235
 }
... ...
@@ -344,12 +345,12 @@ blobGrow(blob *b, size_t len)
344 344
 
345 345
 		b->data = cli_malloc(len);
346 346
 		if(b->data)
347
-			b->size = len;
347
+			b->size = (off_t)len;
348 348
 	} else {
349 349
 		unsigned char *ptr = cli_realloc(b->data, b->size + len);
350 350
 
351 351
 		if(ptr) {
352
-			b->size += len;
352
+			b->size += (off_t)len;
353 353
 			b->data = ptr;
354 354
 		}
355 355
 	}
... ...
@@ -371,6 +372,52 @@ fileblobCreate(void)
371 371
 #endif
372 372
 }
373 373
 
374
+/*
375
+ * Returns CL_CLEAN or CL_VIRUS. Destroys the fileblob and removes the file
376
+ * if possible
377
+ */
378
+int
379
+fileblobScanAndDestroy(fileblob *fb)
380
+{
381
+	switch(fileblobScan(fb)) {
382
+		case CL_VIRUS:
383
+			fileblobDestructiveDestroy(fb);
384
+			return CL_VIRUS;
385
+		case CL_BREAK:
386
+			fileblobDestructiveDestroy(fb);
387
+			return CL_CLEAN;
388
+		default:
389
+			fileblobDestroy(fb);
390
+			return CL_CLEAN;
391
+	}
392
+}
393
+
394
+/*
395
+ * Destroy the fileblob, and remove the file associated with it
396
+ */
397
+void
398
+fileblobDestructiveDestroy(fileblob *fb)
399
+{
400
+	if(fb->fp && fb->fullname) {
401
+		fclose(fb->fp);
402
+		cli_dbgmsg("fileblobDestructiveDestroy: %s\n", fb->fullname);
403
+		if(unlink(fb->fullname) < 0)
404
+			cli_warnmsg("fileblobDestructiveDestroy: Can't delete file %s\n", fb->fullname);
405
+		free(fb->fullname);
406
+		fb->fp = NULL;
407
+		fb->fullname = NULL;
408
+	}
409
+	if(fb->b.name) {
410
+		free(fb->b.name);
411
+		fb->b.name = NULL;
412
+	}
413
+	fileblobDestroy(fb);
414
+}
415
+
416
+/*
417
+ * Destroy the fileblob, and remove the file associated with it if that file is
418
+ * empty
419
+ */
374 420
 void
375 421
 fileblobDestroy(fileblob *fb)
376 422
 {
... ...
@@ -384,7 +431,7 @@ fileblobDestroy(fileblob *fb)
384 384
 			if(!fb->isNotEmpty) {
385 385
 				cli_dbgmsg("fileblobDestroy: not saving empty file\n");
386 386
 				if(unlink(fb->fullname) < 0)
387
-					cli_warnmsg("fileblobDestroy: Can't delete empty files %s\n", fb->fullname);
387
+					cli_warnmsg("fileblobDestroy: Can't delete empty file %s\n", fb->fullname);
388 388
 			}
389 389
 		}
390 390
 		free(fb->b.name);
... ...
@@ -571,10 +618,62 @@ fileblobSetCTX(fileblob *fb, cli_ctx *ctx)
571 571
 	fb->ctx = ctx;
572 572
 }
573 573
 
574
+/*
575
+ * Performs a full scan on the fileblob, returning ClamAV status:
576
+ *	CL_BREAK means clean
577
+ *	CL_CLEAN means unknown
578
+ *	CL_VIRUS means infected
579
+ */
580
+int
581
+fileblobScan(const fileblob *fb)
582
+{
583
+	int rc, fd;
584
+
585
+	if(fb->isInfected)
586
+		return CL_VIRUS;
587
+	if(fb->fullname == NULL) {
588
+		/* shouldn't happen, scan called before fileblobSetFilename */
589
+		cli_warnmsg("fileblobScan, fullname == NULL\n");
590
+		return CL_CLEAN;	/* there is no CL_UNKNOWN */
591
+	}
592
+	if(fb->ctx == NULL) {
593
+		/* fileblobSetCTX hasn't been called */
594
+		cli_dbgmsg("fileblobScan, ctx == NULL\n");
595
+		return CL_CLEAN;	/* there is no CL_UNKNOWN */
596
+	}
597
+
598
+	fflush(fb->fp);
599
+	fd = dup(fileno(fb->fp));
600
+	if(fd == -1) {
601
+		cli_warnmsg("%s: dup failed\n", fb->fullname);
602
+		return CL_CLEAN;
603
+	}
604
+	/* cli_scanfile is static :-( */
605
+	/*if(cli_scanfile(fb->fullname, fb->ctx) == CL_VIRUS) {
606
+		printf("%s is infected\n", fb->fullname);
607
+		return CL_VIRUS;
608
+	}*/
609
+
610
+	rc = cli_magic_scandesc(fd, fb->ctx);
611
+	close(fd);
612
+	if(rc == CL_VIRUS) {
613
+		cli_dbgmsg("%s is infected\n", fb->fullname);
614
+		return CL_VIRUS;
615
+	}
616
+	cli_dbgmsg("%s is clean\n", fb->fullname);
617
+	return CL_BREAK;
618
+
619
+	/*return cli_scanfile(fb->fullname, fb->ctx);*/
620
+}
621
+
622
+/*
623
+ * Doesn't perform a full scan just lets the caller know if something suspicious has
624
+ * been seen yet
625
+ */
574 626
 int
575
-fileblobContainsVirus(const fileblob *fb)
627
+fileblobInfected(const fileblob *fb)
576 628
 {
577
-	return fb->isInfected ? TRUE : FALSE;
629
+	return fb->isInfected;
578 630
 }
579 631
 
580 632
 /*
... ...
@@ -62,12 +62,15 @@ typedef	struct fileblob {
62 62
 } fileblob;
63 63
 
64 64
 fileblob	*fileblobCreate(void);
65
+int	fileblobScanAndDestroy(fileblob *fb);
66
+void	fileblobDestructiveDestroy(fileblob *fb);
65 67
 void	fileblobDestroy(fileblob *fb);
66 68
 void	fileblobSetFilename(fileblob *fb, const char *dir, const char *filename);
67 69
 const	char	*fileblobGetFilename(const fileblob *fb);
68 70
 void	fileblobSetCTX(fileblob *fb, cli_ctx *ctx);
69 71
 int	fileblobAddData(fileblob *fb, const unsigned char *data, size_t len);
70
-int	fileblobContainsVirus(const fileblob *fb);
72
+int	fileblobScan(const fileblob *fb);
73
+int	fileblobInfected(const fileblob *fb);
71 74
 void	sanitiseName(char *name);
72 75
 
73 76
 #endif /*_BLOB_H*/
... ...
@@ -257,7 +257,7 @@ static	void	*getURL(struct arg *arg);
257 257
 #endif
258 258
 static	long	nonblock_fcntl(int sock);
259 259
 static	void	restore_fcntl(int sock, long fcntl_flags);
260
-static	int	nonblock_connect(const char *url, int sock, const struct sockaddr *addr, socklen_t addrlen, int secs);
260
+static	int	nonblock_connect(const char *url, int sock, const struct sockaddr *addr);
261 261
 static	int	connect_error(const char *url, int sock);
262 262
 static	int	my_r_gethostbyname(const char *hostname, struct hostent *hp, char *buf, size_t len);
263 263
 
... ...
@@ -2475,7 +2475,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re
2475 2475
 
2476 2476
 			cli_dbgmsg("The message has %d parts\n", multiparts);
2477 2477
 
2478
-			if(((multiparts == 0) || infected) && (aText == NULL)) {
2478
+			if(infected || ((multiparts == 0) && (aText == NULL))) {
2479 2479
 				if(messages) {
2480 2480
 					for(i = 0; i < multiparts; i++)
2481 2481
 						if(messages[i])
... ...
@@ -2537,6 +2537,10 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re
2537 2537
 						messageDestroy(aMessage);
2538 2538
 						messages[htmltextPart] = NULL;
2539 2539
 					}
2540
+					else if(rc == VIRUS) {
2541
+						infected = TRUE;
2542
+						break;
2543
+					}
2540 2544
 				}
2541 2545
 
2542 2546
 				/*
... ...
@@ -2751,7 +2755,8 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re
2751 2751
 
2752 2752
 				if(fb) {
2753 2753
 					cli_dbgmsg("Saving main message as attachment\n");
2754
-					fileblobDestroy(fb);
2754
+					if(fileblobScanAndDestroy(fb) == CL_VIRUS)
2755
+						rc = VIRUS;
2755 2756
 					if(mainMessage != messageIn) {
2756 2757
 						messageDestroy(mainMessage);
2757 2758
 						mainMessage = NULL;
... ...
@@ -2783,7 +2788,7 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re
2783 2783
 		/* Look for a bounce in the text (non mime encoded) portion */
2784 2784
 		const text *t;
2785 2785
 
2786
-		for(t = aText; t; t = t->t_next) {
2786
+		for(t = aText; t && (rc != VIRUS); t = t->t_next) {
2787 2787
 			const line_t *l = t->t_line;
2788 2788
 			const text *lookahead, *topofbounce;
2789 2789
 			const char *s;
... ...
@@ -2896,9 +2901,11 @@ parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int re
2896 2896
 						break;
2897 2897
 					}
2898 2898
 				}
2899
-			} while(!fileblobContainsVirus(fb));
2899
+			} while(!fileblobInfected(fb));
2900
+
2901
+			if(fileblobScanAndDestroy(fb) == CL_VIRUS)
2902
+				rc = VIRUS;
2900 2903
 
2901
-			fileblobDestroy(fb);
2902 2904
 			if(topofbounce)
2903 2905
 				t = topofbounce;
2904 2906
 			/*
... ...
@@ -4197,13 +4204,13 @@ getURL(struct arg *arg)
4197 4197
 
4198 4198
 		memcpy((char *)&ip, h.h_addr, sizeof(ip));
4199 4199
 	}
4200
-	server.sin_addr.s_addr = ip;
4201 4200
 	if((sd = socket(AF_INET, SOCK_STREAM, tcp)) < 0) {
4202 4201
 		fclose(fp);
4203 4202
 		return NULL;
4204 4203
 	}
4204
+	server.sin_addr.s_addr = ip;
4205 4205
 	flags = nonblock_fcntl(sd);
4206
-	if(nonblock_connect(url, sd, (struct sockaddr *)&server, sizeof(struct sockaddr_in), URL_TIMEOUT) < 0) {
4206
+	if(nonblock_connect(url, sd, (struct sockaddr *)&server) < 0) {
4207 4207
 		closesocket(sd);
4208 4208
 		fclose(fp);
4209 4209
 		return NULL;
... ...
@@ -4440,7 +4447,7 @@ restore_fcntl(int sock, long fcntl_flags)
4440 4440
 }
4441 4441
 
4442 4442
 static int
4443
-nonblock_connect(const char *url, int sock, const struct sockaddr *addr, socklen_t addrlen, int secs)
4443
+nonblock_connect(const char *url, int sock, const struct sockaddr *addr)
4444 4444
 {
4445 4445
 	int select_failures;	/* Max. of unexpected select() failures */
4446 4446
 	int bogus_loops;	/* Max. of useless loops */
... ...
@@ -4450,7 +4457,7 @@ nonblock_connect(const char *url, int sock, const struct sockaddr *addr, socklen
4450 4450
 	/* Calculate into 'timeout' when we should time out */
4451 4451
 	gettimeofday(&timeout, 0);
4452 4452
 
4453
-	if(connect(sock, addr, addrlen))
4453
+	if(connect(sock, addr, sizeof(struct sockaddr_in)) != 0)
4454 4454
 		switch(errno) {
4455 4455
 			case EALREADY:
4456 4456
 			case EINPROGRESS:
... ...
@@ -4468,7 +4475,7 @@ nonblock_connect(const char *url, int sock, const struct sockaddr *addr, socklen
4468 4468
 	numfd = sock + 1; /* Highest fdset fd plus 1 */
4469 4469
 	select_failures = NONBLOCK_SELECT_MAX_FAILURES;
4470 4470
 	bogus_loops = NONBLOCK_MAX_BOGUS_LOOPS;
4471
-	timeout.tv_sec += secs;
4471
+	timeout.tv_sec += URL_TIMEOUT;
4472 4472
 
4473 4473
 	for (;;) {
4474 4474
 		int n, t;
... ...
@@ -4483,7 +4490,7 @@ nonblock_connect(const char *url, int sock, const struct sockaddr *addr, socklen
4483 4483
 
4484 4484
 		if(t) {
4485 4485
 			cli_warnmsg("%s: connect timeout (%d secs)\n",
4486
-				url, secs);
4486
+				url, URL_TIMEOUT);
4487 4487
 			break;
4488 4488
 		}
4489 4489
 
... ...
@@ -4716,12 +4723,11 @@ exportBinhexMessage(const char *dir, message *m)
4716 4716
 	fb = messageToFileblob(m, dir, 0);
4717 4717
 
4718 4718
 	if(fb) {
4719
-		if(fileblobContainsVirus(fb))
4720
-			infected = TRUE;
4721
-
4722 4719
 		cli_dbgmsg("Binhex file decoded to %s\n",
4723 4720
 			fileblobGetFilename(fb));
4724
-		fileblobDestroy(fb);
4721
+
4722
+		if(fileblobScanAndDestroy(fb) == CL_VIRUS)
4723
+			infected = TRUE;
4725 4724
 	} else
4726 4725
 		cli_errmsg("Couldn't decode binhex file to %s\n", dir);
4727 4726
 
... ...
@@ -5011,20 +5017,20 @@ do_multipart(message *mainMessage, message **messages, int i, mbox_status *rc, m
5011 5011
 			return mainMessage;
5012 5012
 	}
5013 5013
 
5014
-	if(addToText) {
5015
-		cli_dbgmsg("Adding to non mime-part\n");
5016
-		*tptr = textAdd(*tptr, messageGetBody(aMessage));
5017
-	} else {
5018
-		fileblob *fb = messageToFileblob(aMessage, mctx->dir, 1);
5014
+	if(*rc != VIRUS) {
5015
+		if(addToText) {
5016
+			cli_dbgmsg("Adding to non mime-part\n");
5017
+			*tptr = textAdd(*tptr, messageGetBody(aMessage));
5018
+		} else {
5019
+			fileblob *fb = messageToFileblob(aMessage, mctx->dir, 1);
5019 5020
 
5020
-		if(fb) {
5021
-			if(fileblobContainsVirus(fb))
5022
-				*rc = VIRUS;
5023
-			fileblobDestroy(fb);
5021
+			if(fb)
5022
+				if(fileblobScanAndDestroy(fb) == CL_VIRUS)
5023
+					*rc = VIRUS;
5024 5024
 		}
5025
+		if(messageContainsVirus(aMessage))
5026
+			*rc = VIRUS;
5025 5027
 	}
5026
-	if(messageContainsVirus(aMessage))
5027
-		*rc = VIRUS;
5028 5028
 	messageDestroy(aMessage);
5029 5029
 	messages[i] = NULL;
5030 5030