Browse code

Added -I flag and blacklist

git-svn: trunk@2068

Nigel Horne authored on 2006/07/12 00:29:16
Showing 3 changed files
... ...
@@ -1,7 +1,13 @@
1
+Tue Jul 11 16:26:46 BST 2006 (njh)
2
+----------------------------------
3
+  * clamav-milter:	Added -I flag based on an idea by
4
+				Dugal James P. <jpd*louisiana.edu>
5
+			Added first draft of blacklist support
6
+
1 7
 Mon Jul 10 19:40:59 BST 2006 (njh)
2 8
 ----------------------------------
3 9
   * libclamav/mbox.c:	Some HTML.Phishing.Bank-566 were not being found, bug
4
-  				reported by Sven
10
+				reported by Sven
5 11
 
6 12
 Sat Jul  8 20:57:31 BST 2006 (njh)
7 13
 ----------------------------------
... ...
@@ -23,7 +23,7 @@
23 23
  *
24 24
  * For installation instructions see the file INSTALL that came with this file
25 25
  */
26
-static	char	const	rcsid[] = "$Id: clamav-milter.c,v 1.247 2006/06/21 09:09:59 njh Exp $";
26
+static	char	const	rcsid[] = "$Id: clamav-milter.c,v 1.248 2006/07/11 15:25:26 njh Exp $";
27 27
 
28 28
 #define	CM_VERSION	"devel-210606"
29 29
 
... ...
@@ -82,6 +82,7 @@ static	char	const	rcsid[] = "$Id: clamav-milter.c,v 1.247 2006/06/21 09:09:59 nj
82 82
 #include <sys/sendfile.h>	/* FIXME: use sendfile on BSD not Linux */
83 83
 #include <libintl.h>
84 84
 #include <locale.h>
85
+#include <resolv.h>
85 86
 
86 87
 #define	gettext_noop(s)	s
87 88
 #define	_(s)	gettext(s)
... ...
@@ -195,6 +196,7 @@ static const struct cidr_net {
195 195
 	{ PACKADDR( 10,   0,   0,   0), MAKEMASK(24) },	/*    10.0.0.0/24 */
196 196
 	{ PACKADDR(172,  16,   0,   0), MAKEMASK(20) },	/*  172.16.0.0/20 */
197 197
 	{ PACKADDR(169,  254,  0,   0), MAKEMASK(16) },	/* 169.254.0.0/16 */
198
+	{ 0, 0 },	/* space to put one more via -I addr */
198 199
 	{ 0, 0 }
199 200
 };
200 201
 
... ...
@@ -205,8 +207,8 @@ struct	privdata {
205 205
 	char	*from;	/* Who sent the message */
206 206
 	char	*subject;	/* Original subject */
207 207
 	char	*sender;	/* Secretary - often used in mailing lists */
208
-	char	*helo;		/* The HELO string */
209 208
 	char	**to;	/* Who is the message going to */
209
+	char	*ip;	/* IP address of the other end */
210 210
 	int	numTo;	/* Number of people the message is going to */
211 211
 #ifndef	SESSION
212 212
 	int	cmdSocket;	/*
... ...
@@ -285,6 +287,7 @@ static	char	clamav_version[VERSION_LENGTH + 1];
285 285
 static	int	fflag = 0;	/* force a scan, whatever */
286 286
 static	int	oflag = 0;	/* scan messages from our machine? */
287 287
 static	int	lflag = 0;	/* scan messages from our site? */
288
+static	int	Iflag = 0;	/* Added an IP addr to localNets? */
288 289
 static	const	char	*progname;	/* our name - usually clamav-milter */
289 290
 
290 291
 /* Variables for --external */
... ...
@@ -445,6 +448,10 @@ static	const	char	*whitelistFile;	/*
445 445
 static	const	char	*sendmailCF;	/* location of sendmail.cf to verify */
446 446
 static	const	char	*pidfile;
447 447
 
448
+static	table_t	*blacklist;	/* never freed */
449
+static	int	blacklist_time;	/* How long to blacklist an IP */
450
+static	pthread_mutex_t	blacklist_mutex = PTHREAD_MUTEX_INITIALIZER;
451
+
448 452
 #ifdef	CL_DEBUG
449 453
 #if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1
450 454
 #define HAVE_BACKTRACE
... ...
@@ -464,7 +471,10 @@ static	void	print_trace(void);
464 464
 
465 465
 static	int	verifyIncomingSocketName(const char *sockName);
466 466
 static	int	isWhitelisted(const char *emailaddress);
467
+static	int	isBlacklisted(const char *ip_address);
467 468
 static	void	logger(const char *mess);
469
+static	void	mx(void);
470
+static	void	resolve(const char *host);
468 471
 
469 472
 short	logg_time, logg_lock, logok;
470 473
 int	logg_size;
... ...
@@ -476,6 +486,7 @@ help(void)
476 476
 	puts("\tCopyright (C) 2006 Nigel Horne <njh@clamav.net>\n");
477 477
 
478 478
 	puts(_("\t--advisory\t\t-A\tFlag viruses rather than deleting them."));
479
+	puts(_("\t--blacklist=time\t-k\tTime (in seconds) to blacklist an IP."));
479 480
 	puts(_("\t--bounce\t\t-b\tSend a failure message to the sender."));
480 481
 	puts(_("\t--broadcast\t\t-B [IFACE]\tBroadcast to a network manager when a virus is found."));
481 482
 	puts(_("\t--config-file=FILE\t-c FILE\tRead configuration from FILE."));
... ...
@@ -490,6 +501,7 @@ help(void)
490 490
 	puts(_("\t--force-scan\t\t-f\tForce scan all messages (overrides (-o and -l)."));
491 491
 	puts(_("\t--help\t\t\t-h\tThis message."));
492 492
 	puts(_("\t--headers\t\t-H\tInclude original message headers in the report."));
493
+	puts(_("\t--ignore IPaddr\t\t-I IPaddr\tAdd IPaddr to LAN IP list (see --local)."));
493 494
 	puts(_("\t--local\t\t\t-l\tScan messages sent from machines on our LAN."));
494 495
 	puts(_("\t--max-childen\t\t-m\tMaximum number of concurrent scans."));
495 496
 	puts(_("\t--outgoing\t\t-o\tScan outgoing messages from this machine."));
... ...
@@ -556,6 +568,7 @@ main(int argc, char **argv)
556 556
 	if(setrlimit(RLIMIT_CORE, &rlim) < 0)
557 557
 		perror("setrlimit");
558 558
 #endif
559
+
559 560
 	/*
560 561
 	 * Temporarily enter guessed value into version, will
561 562
 	 * be overwritten later by the value returned by clamd
... ...
@@ -578,10 +591,12 @@ main(int argc, char **argv)
578 578
 
579 579
 	for(;;) {
580 580
 		int opt_index = 0;
581
+		struct cidr_net *net;
582
+		struct in_addr ignoreIP;
581 583
 #ifdef	CL_DEBUG
582
-		const char *args = "a:AbB:c:CdDefF:lLm:M:nNop:PqQ:hHs:St:T:U:VwW:x:0:";
584
+		const char *args = "a:AbB:c:CdDefF:I:k:lLm:M:nNop:PqQ:hHs:St:T:U:VwW:x:0:";
583 585
 #else
584
-		const char *args = "a:AbB:c:CdDefF:lLm:M:nNop:PqQ:hHs:St:T:U:VwW:0:";
586
+		const char *args = "a:AbB:c:CdDefF:I:k:lLm:M:nNop:PqQ:hHs:St:T:U:VwW:0:";
585 587
 #endif
586 588
 
587 589
 		static struct option long_options[] = {
... ...
@@ -628,9 +643,15 @@ main(int argc, char **argv)
628 628
 				"help", 0, NULL, 'h'
629 629
 			},
630 630
 			{
631
+ 				"ignore", 1, NULL, 'I'
632
+ 			},
633
+			{
631 634
 				"pidfile", 1, NULL, 'i'
632 635
 			},
633 636
 			{
637
+				"blacklist", 1, NULL, 'k'
638
+			},
639
+			{
634 640
 				"local", 0, NULL, 'l'
635 641
 			},
636 642
 			{
... ...
@@ -754,6 +775,32 @@ main(int argc, char **argv)
754 754
 			case 'i':	/* pidfile */
755 755
 				pidfile = optarg;
756 756
 				break;
757
+			case 'k':	/* blacklist time */
758
+				blacklist_time = atoi(optarg);
759
+				break;
760
+			case 'I':	/* --ignore, -I hostname */
761
+				/*
762
+				 * Based on patch by jpd@louisiana.edu
763
+				 */
764
+				if(Iflag) {
765
+ 					fprintf(stderr,
766
+						_("%s: %s, -I may only be given once"),
767
+							argv[0], optarg);
768
+ 					return EX_USAGE;
769
+				}
770
+				if(!inet_aton(optarg, &ignoreIP)) {
771
+ 					fprintf(stderr,
772
+						_("%s: Cannot convert -I%s to IPaddr"),
773
+							argv[0], optarg);
774
+ 					return EX_USAGE;
775
+ 				}
776
+                                for(net = (struct cidr_net *)localNets; net->base; net++)
777
+					;
778
+				/* TODO: allow netmasks */
779
+				net->base = ntohl(ignoreIP.s_addr);
780
+				net->mask = ntohl(0xffffffffU);
781
+ 				Iflag++;
782
+ 				break;
757 783
 			case 'l':	/* scan mail from the lan */
758 784
 				lflag++;
759 785
 				break;
... ...
@@ -878,7 +925,7 @@ main(int argc, char **argv)
878 878
 	 * Sanity checks on the clamav configuration file
879 879
 	 */
880 880
 	if(cfgfile == NULL) {
881
-		cfgfile = cli_malloc(strlen(CONFDIR) + 12);
881
+		cfgfile = cli_malloc(strlen(CONFDIR) + 12);	/* leak */
882 882
 		sprintf(cfgfile, "%s/clamd.conf", CONFDIR);
883 883
 	}
884 884
 	if((copt = getcfg(cfgfile, 1)) == NULL) {
... ...
@@ -1639,6 +1686,13 @@ main(int argc, char **argv)
1639 1639
 #endif
1640 1640
 	}
1641 1641
 
1642
+	if(blacklist_time) {
1643
+		mx();
1644
+		if(blacklist)
1645
+			/* We must never blacklist ourself */
1646
+			tableInsert(blacklist, "127.0.0.1", 0);
1647
+	}
1648
+
1642 1649
 	cli_dbgmsg("Started: %s\n", clamav_version);
1643 1650
 #ifdef	SESSION
1644 1651
 	pthread_mutex_unlock(&version_mutex);
... ...
@@ -1767,19 +1821,26 @@ pingServer(int serverNumber)
1767 1767
 			return 0;
1768 1768
 		}
1769 1769
 		if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
1770
-			/*
1771
-			 * During startup there is a race condition: clamd can
1772
-			 * start and fork, then rc will start clamav-milter
1773
-			 * before clamd has run accept(2), so we fail to
1774
-			 * connect. In case this is the situation here, we wait
1775
-			 * for a couple of seconds and try again. The sync() is
1776
-			 * because during startup the machine won't be doing
1777
-			 * much for most of the time, so we may as well do
1778
-			 * something constructive!
1779
-			 */
1780
-			sync();
1781
-			sleep(2);
1782
-			if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
1770
+			int is_connected = 0;
1771
+
1772
+			if(errno == ECONNREFUSED) {
1773
+				/*
1774
+				 * During startup there is a race condition:
1775
+				 * clamd can start and fork, then rc will start
1776
+				 * clamav-milter before clamd has run accept(2),
1777
+				 * so we fail to connect.
1778
+				 * In case this is the situation here, we wait
1779
+				 * for a couple of seconds and try again. The
1780
+				 * sync() is because during startup the machine
1781
+				 * won't be doing much for most of the time, so
1782
+				 * we may as well do something constructive!
1783
+				 */
1784
+				sync();
1785
+				sleep(2);
1786
+				if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) >= 0)
1787
+					is_connected = 1;
1788
+			}
1789
+			if(!is_connected) {
1783 1790
 				char *hostname = cli_strtok(serverHostNames,
1784 1791
 					serverNumber, ":");
1785 1792
 
... ...
@@ -1952,6 +2013,14 @@ findServer(void)
1952 1952
 	for(i = 0, server = servers; i < numServers; i++, server++) {
1953 1953
 		int sock;
1954 1954
 
1955
+		if(((i + j) % numServers) >= numServers)
1956
+			/* "can't happen" */
1957
+			if(use_syslog) {
1958
+				syslog(LOG_ERR, "FindServer: looking for %d from %d - report to bugs@clamav.net\n",
1959
+					(i + j) % numServers, numServers);
1960
+				return 0;
1961
+			}
1962
+
1955 1963
 		server->sin_family = AF_INET;
1956 1964
 		server->sin_port = (in_port_t)htons(tcpSocket);
1957 1965
 		server->sin_addr.s_addr = serverIPs[(i + j) % numServers];
... ...
@@ -2059,6 +2128,7 @@ clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
2059 2059
 	char ip[INET_ADDRSTRLEN];	/* IPv4 only */
2060 2060
 #endif
2061 2061
 	const char *remoteIP;
2062
+	struct privdata *privdata;
2062 2063
 
2063 2064
 	if(quitting)
2064 2065
 		return cl_error;
... ...
@@ -2224,7 +2294,37 @@ clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
2224 2224
 			return SMFIS_REJECT;
2225 2225
 		}
2226 2226
 	}
2227
-	return SMFIS_CONTINUE;
2227
+	if(isBlacklisted(remoteIP)) {
2228
+		if(use_syslog)
2229
+			syslog(LOG_NOTICE, "Rejected email from blacklisted IP %s",
2230
+				remoteIP);
2231
+		/*
2232
+		 * TODO: Option to greylist rather than blacklist, by sending
2233
+		 *	a try again code
2234
+		 */
2235
+		smfi_setreply(ctx, "550", "5.7.1", _("Your IP is blacklisted"));
2236
+		broadcast(_("Blacklisted IP detected"));
2237
+		return SMFIS_REJECT;
2238
+	}
2239
+	if(smfi_getpriv(ctx) != NULL) {
2240
+		/* More than one MAIL FROM command, "can't happen" */
2241
+		cli_warnmsg("clamfi_connect: called more than once\n");
2242
+		return SMFIS_TEMPFAIL;
2243
+	}
2244
+
2245
+	privdata = (struct privdata *)cli_calloc(1, sizeof(struct privdata));
2246
+	if(privdata == NULL)
2247
+		return cl_error;
2248
+
2249
+	if(smfi_setpriv(ctx, privdata) == MI_SUCCESS) {
2250
+		if(blacklist_time)
2251
+			privdata->ip = strdup(remoteIP);
2252
+		return SMFIS_CONTINUE;
2253
+	}
2254
+
2255
+	clamfi_free(privdata);
2256
+
2257
+	return cl_error;
2228 2258
 }
2229 2259
 
2230 2260
 /*
... ...
@@ -2262,15 +2362,19 @@ clamfi_envfrom(SMFICTX *ctx, char **argv)
2262 2262
 			mailaddr = "<>";
2263 2263
 		}
2264 2264
 	}
2265
-	if(smfi_getpriv(ctx) != NULL) {
2266
-		/* More than one MAIL FROM command, "can't happen" */
2267
-		cli_warnmsg("clamfi_envfrom: called more than once\n");
2268
-		return SMFIS_CONTINUE;
2269
-	}
2265
+	privdata = smfi_getpriv(ctx);
2270 2266
 
2271
-	privdata = (struct privdata *)cli_calloc(1, sizeof(struct privdata));
2272
-	if(privdata == NULL)
2273
-		return cl_error;
2267
+	if(privdata == NULL) {
2268
+		/* More than one message on this connection */
2269
+		privdata = (struct privdata *)cli_calloc(1, sizeof(struct privdata));
2270
+		if(privdata == NULL)
2271
+			return cl_error;
2272
+
2273
+		if(smfi_setpriv(ctx, privdata) != MI_SUCCESS) {
2274
+			free(privdata);
2275
+			return cl_error;
2276
+		}
2277
+	}
2274 2278
 
2275 2279
 	if(max_children > 0) {
2276 2280
 		int rc = 0;
... ...
@@ -2300,9 +2404,12 @@ clamfi_envfrom(SMFICTX *ctx, char **argv)
2300 2300
 					n_children, max_children);
2301 2301
 
2302 2302
 			if(dont_wait) {
2303
-				free(privdata);
2304 2303
 				pthread_mutex_unlock(&n_children_mutex);
2305 2304
 				smfi_setreply(ctx, "451", "4.3.2", _("AV system temporarily overloaded - please try later"));
2305
+				if(privdata->ip)
2306
+					free(privdata->ip);
2307
+				free(privdata);
2308
+				smfi_setpriv(ctx, NULL);
2306 2309
 				return SMFIS_TEMPFAIL;
2307 2310
 			}
2308 2311
 			/*
... ...
@@ -2371,12 +2478,7 @@ clamfi_envfrom(SMFICTX *ctx, char **argv)
2371 2371
 	if(hflag)
2372 2372
 		privdata->headers = header_list_new();
2373 2373
 
2374
-	if(smfi_setpriv(ctx, privdata) == MI_SUCCESS)
2375
-		return SMFIS_CONTINUE;
2376
-
2377
-	clamfi_free(privdata);
2378
-
2379
-	return cl_error;
2374
+	return SMFIS_CONTINUE;
2380 2375
 }
2381 2376
 
2382 2377
 #ifdef	CL_DEBUG
... ...
@@ -2593,7 +2695,7 @@ clamfi_body(SMFICTX *ctx, u_char *bodyp, size_t len)
2593 2593
 	 *	avoid FP matches in the scanning code, which will speed it up
2594 2594
 	 */
2595 2595
 	if((len >= 6) && cli_memstr((char *)bodyp, len, "\nFrom ", 6)) {
2596
-		const char *ptr = bodyp;
2596
+		const char *ptr = (const char *)bodyp;
2597 2597
 		int left = len;
2598 2598
 
2599 2599
 		nbytes = 0;
... ...
@@ -3285,6 +3387,19 @@ clamfi_eom(SMFICTX *ctx)
3285 3285
 		snprintf(reject, sizeof(reject) - 1, _("virus %s detected by ClamAV - http://www.clamav.net"), virusname);
3286 3286
 		smfi_setreply(ctx, (char *)privdata->rejectCode, "5.7.1", reject);
3287 3287
 		broadcast(mess);
3288
+
3289
+		if(privdata->ip) {
3290
+			pthread_mutex_lock(&blacklist_mutex);
3291
+			/*
3292
+			 * FIXME: should be able to update here, otherwise we
3293
+			 * can't reblacklist an entry that has timed out
3294
+			 */
3295
+			(void)tableInsert(blacklist, privdata->ip,
3296
+				(int)time((time_t *)0));
3297
+			pthread_mutex_unlock(&blacklist_mutex);
3298
+			free(privdata->ip);
3299
+			privdata->ip = NULL;
3300
+		}
3288 3301
 	} else if((strstr(mess, "OK") == NULL) && (strstr(mess, "Empty file") == NULL)) {
3289 3302
 		if(!nflag)
3290 3303
 			smfi_addheader(ctx, "X-Virus-Status", _("Unknown"));
... ...
@@ -3409,6 +3524,11 @@ clamfi_free(struct privdata *privdata)
3409 3409
 			privdata->filename = NULL;
3410 3410
 		}
3411 3411
 
3412
+		if(privdata->ip) {
3413
+			free(privdata->ip);
3414
+			privdata->ip = NULL;
3415
+		}
3416
+
3412 3417
 		if(privdata->from) {
3413 3418
 #ifdef	CL_DEBUG
3414 3419
 			if(debug_level >= 9)
... ...
@@ -5283,7 +5403,7 @@ verifyIncomingSocketName(const char *sockName)
5283 5283
 static int
5284 5284
 isWhitelisted(const char *emailaddress)
5285 5285
 {
5286
-	static table_t *whitelist;
5286
+	static table_t *whitelist;	/* never freed */
5287 5287
 
5288 5288
 	cli_dbgmsg("isWhitelisted %s\n", emailaddress);
5289 5289
 
... ...
@@ -5302,12 +5422,19 @@ isWhitelisted(const char *emailaddress)
5302 5302
 		if(fin == NULL) {
5303 5303
 			perror(whitelistFile);
5304 5304
 			if(use_syslog)
5305
-				syslog(LOG_ERR, _("Can't open white-list file %s"),
5305
+				syslog(LOG_ERR, _("Can't open whitelist file %s"),
5306 5306
 					whitelistFile);
5307 5307
 			return 0;
5308 5308
 		}
5309 5309
 		whitelist = tableCreate();
5310 5310
 
5311
+		if(whitelist == NULL) {
5312
+			if(use_syslog)
5313
+				syslog(LOG_ERR, _("Can't create whitelist table"));
5314
+			fclose(fin);
5315
+			return 0;
5316
+		}
5317
+
5311 5318
 		while(fgets(buf, sizeof(buf), fin) != NULL) {
5312 5319
 			/* comment line? */
5313 5320
 			switch(buf[0]) {
... ...
@@ -5330,6 +5457,64 @@ isWhitelisted(const char *emailaddress)
5330 5330
 	return 0;
5331 5331
 }
5332 5332
 
5333
+/*
5334
+ * Blacklist IP addresses that send malware. Often in the phishing world, one
5335
+ * phish is quickly followed by another. IP addresses are blacklisted for one
5336
+ * minute. We can't blacklist for longer since DHCP means we could hit innocent
5337
+ * parties, and in theory malware could go through a smart host and affect
5338
+ * innocent parties
5339
+ *
5340
+ * Note that sites which can't be blacklisted will have their timestamp set
5341
+ * to 0, since that can never be less than blacklist_time seconds from now
5342
+ */
5343
+static int
5344
+isBlacklisted(const char *ip_address)
5345
+{
5346
+	time_t t, now;
5347
+
5348
+	if(blacklist_time == 0)
5349
+		/* Blacklisting not being used */
5350
+		return 0;
5351
+
5352
+	cli_dbgmsg("isBlacklisted %s\n", ip_address);
5353
+
5354
+	if(isLocalAddr(inet_addr(ip_address)))
5355
+		return 0;
5356
+
5357
+	time(&now);
5358
+
5359
+	pthread_mutex_lock(&blacklist_mutex);
5360
+	if(blacklist == NULL) {
5361
+		blacklist = tableCreate();
5362
+
5363
+		if(blacklist == NULL) {
5364
+			if(use_syslog)
5365
+				syslog(LOG_ERR, _("Can't create blacklist table"));
5366
+			pthread_mutex_unlock(&blacklist_mutex);
5367
+			return 0;
5368
+		}
5369
+		pthread_mutex_unlock(&blacklist_mutex);
5370
+		return 0;
5371
+	}
5372
+	t = tableFind(blacklist, ip_address);
5373
+	pthread_mutex_unlock(&blacklist_mutex);
5374
+
5375
+	if(t == (time_t)-1)
5376
+		/* IP address is not blacklisted */
5377
+		return 0;
5378
+
5379
+	if(t == (time_t)0)
5380
+		/* IP cannot be blacklisted */
5381
+		return 0;
5382
+
5383
+	if((now - t) < blacklist_time)
5384
+		/* FIXME: should be able to renew the certificate */
5385
+		return 1;
5386
+
5387
+	/* FIXME: should be able to remove the certificate */
5388
+	return 0;
5389
+}
5390
+
5333 5391
 static void
5334 5392
 logger(const char *mess)
5335 5393
 {
... ...
@@ -5367,3 +5552,141 @@ logger(const char *mess)
5367 5367
 		fclose(fout);
5368 5368
 #endif
5369 5369
 }
5370
+
5371
+/*
5372
+ * Determine our MX peers, they must never be blacklisted
5373
+ * See RFC1034 for the definition of the record formats
5374
+ */
5375
+static void
5376
+mx(void)
5377
+{
5378
+	const u_char *p, *end;
5379
+	char name[MAXHOSTNAMELEN + 1];
5380
+	u_char buf[BUFSIZ];
5381
+	union {
5382
+		HEADER h;
5383
+		u_char	u[PACKETSZ];
5384
+	} q;
5385
+	const HEADER *hp;
5386
+	int len, i;
5387
+	u_short type, pref;
5388
+	u_long ttl;
5389
+
5390
+	if(gethostname(name, sizeof(name)) < 0) {
5391
+		perror("gethostname");
5392
+		return;
5393
+	}
5394
+
5395
+	if(blacklist == NULL) {
5396
+		blacklist = tableCreate();
5397
+
5398
+		if(blacklist == NULL)
5399
+			return;
5400
+	}
5401
+
5402
+	len = res_query(name, C_IN, T_MX, (u_char *)&q, sizeof(q));
5403
+	if(len < 0)
5404
+		/* Host has no MX records */
5405
+		return;
5406
+
5407
+	if((unsigned int)len > sizeof(q))
5408
+		return;
5409
+
5410
+	hp = &(q.h);
5411
+	p = q.u + HFIXEDSZ;
5412
+	end = q.u + len;
5413
+
5414
+	for(i = ntohs(hp->qdcount); i--; p += len + QFIXEDSZ)
5415
+		if((len = dn_skipname(p, end)) < 0)
5416
+			return;
5417
+
5418
+	i = ntohs(hp->ancount);
5419
+
5420
+	while((--i >= 0) && (p < end)) {
5421
+		long addr;
5422
+
5423
+		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0)
5424
+			break;
5425
+		p += len;
5426
+		GETSHORT(type, p);
5427
+		p += INT16SZ;
5428
+		GETLONG(ttl, p);
5429
+		GETSHORT(len, p);
5430
+		if(type != T_MX) {
5431
+			p += len;
5432
+			continue;
5433
+		}
5434
+		GETSHORT(pref, p);
5435
+		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0)
5436
+			break;
5437
+		p += len;
5438
+		addr = inet_addr(buf);
5439
+		if(addr != -1L) {
5440
+			if(use_syslog)
5441
+				syslog(LOG_INFO, "Won't blacklist %s", buf);
5442
+			(void)tableInsert(blacklist, buf, 0);
5443
+		} else
5444
+			resolve(buf);
5445
+	}
5446
+}
5447
+
5448
+static void
5449
+resolve(const char *host)
5450
+{
5451
+	const u_char *p, *end;
5452
+	u_char buf[BUFSIZ];
5453
+	union {
5454
+		HEADER h;
5455
+		u_char	u[PACKETSZ];
5456
+	} q;
5457
+	const HEADER *hp;
5458
+	int len, i;
5459
+	u_short type;
5460
+	u_long ttl;
5461
+
5462
+	if((host == NULL) || (*host == '\0'))
5463
+		return;
5464
+
5465
+	len = res_query(host, C_IN, T_A, (u_char *)&q, sizeof(q));
5466
+	if(len < 0)
5467
+		/* Host has no A records */
5468
+		return;
5469
+
5470
+	if((unsigned int)len > sizeof(q))
5471
+		return;
5472
+
5473
+	hp = &(q.h);
5474
+	p = q.u + HFIXEDSZ;
5475
+	end = q.u + len;
5476
+
5477
+	for(i = ntohs(hp->qdcount); i--; p += len + QFIXEDSZ)
5478
+		if((len = dn_skipname(p, end)) < 0)
5479
+			return;
5480
+
5481
+	i = ntohs(hp->ancount);
5482
+
5483
+	while((--i >= 0) && (p < end)) {
5484
+		struct in_addr addr;
5485
+		const char *ip;
5486
+
5487
+		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0)
5488
+			 return;
5489
+		p += len;
5490
+		GETSHORT(type, p);
5491
+		p += INT16SZ;
5492
+		GETLONG(ttl, p);
5493
+		GETSHORT(len, p);
5494
+		if(type != T_A) {
5495
+			p += len;
5496
+			continue;
5497
+		}
5498
+		memcpy(&addr, p, sizeof(struct in_addr));
5499
+		p += 4;
5500
+		ip = inet_ntoa(addr);
5501
+		if(ip) {
5502
+			if(use_syslog)
5503
+				syslog(LOG_INFO, "Won't blacklist %s", ip);
5504
+			(void)tableInsert(blacklist, ip, 0);
5505
+		}
5506
+	}
5507
+}
... ...
@@ -102,6 +102,19 @@ The \-\-external option informs clamav\-milter to use an external program such
102 102
 as clamd(8) running either on the local server or other server(s) to perform
103 103
 the scanning.
104 104
 .TP
105
+\fB\-k, \-\-blacklist-time=time\fR
106
+Tells the number in seconds to black list an IP address (IPv4 only). This
107
+is especially useful with phishing which often send a number of emails one
108
+after the other.
109
+.TP
110
+Blacklisting speeds up scanning significantly, however it does have drawbacks
111
+since it is possible for a site to be incorrectly blacklisted because of DHCP
112
+or an unsafe smart-host.
113
+To avoid this, blacklisting does not last for ever. The recommended value is
114
+60.
115
+.TP
116
+Machines on the LAN, the local host, and machines that are our MX peers are
117
+never blacklisted.
105 118
 \fB-l, \-\-local\fR
106 119
 Also scan messages sent from LAN. You probably want this especially if
107 120
 your LAN is populated by machines running Windows or DOS.
... ...
@@ -109,6 +122,7 @@ your LAN is populated by machines running Windows or DOS.
109 109
 Machines with IP addresses within the ranges 192.168.0.0/16, 10.0.0.0/24,
110 110
 172.16.0.0/20 and 169.254.0.0/16 are defined as 'local'. Messages from
111 111
 other machines are always scanned.
112
+An extra IP address may be added with the \-\-ignore option.
112 113
 .TP
113 114
 \fB-M, \-\-freshclam-monitor\fR
114 115
 When not running in external mode, this option tells clamav\-milter how
... ...
@@ -207,6 +221,10 @@ Usually clamav\-milter waits until a child dies or the timeout value has been
207 207
 exceeded, which ever comes first, however with dont-wait enabled, clamav\-milter
208 208
 will inform the remote SMTP client to retry later.
209 209
 .TP
210
+\fB\-\-ignore ipAddr\fR
211
+\fIipAddr\fR is taken to be an extra IPv4 address which is treated as being on
212
+the LAN for the purposes of the \-\-local argument.
213
+.TP
210 214
 \fB\-\-template\-file=file \-t file\fR
211 215
 File points to a file whose contents is sent as the warning message whenever a
212 216
 virus is intercepted.