Browse code

Support multiple clamd servers

git-svn-id: file:///var/lib/svn/clamav-devel/trunk/clamav-devel@205 77e5149b-7576-45b1-b177-96237e5ba77b

Nigel Horne authored on 2004/01/25 23:28:27
Showing 2 changed files
... ...
@@ -1,3 +1,10 @@
1
+Sun Jan 25 14:27:26 GMT 2004 (njh)
2
+----------------------------------
3
+  * clamav-milter: Corrected usage message
4
+		Support multiple servers separated by colons
5
+		Started to honour --debug
6
+		Dump core on LINUX if CL_DEBUG set
7
+
1 8
 Sun Jan 25 07:31:00 CET 2004 (tk)
2 9
 ---------------------------------
3 10
   * libclamav: VBA wrapper - fixed NULL dereference in new code (reported
... ...
@@ -208,9 +208,16 @@
208 208
  *	0.66e	12/1/04	FixStaleSocket: no longer complain if asked to remove
209 209
  *			an old socket when there was none to remove
210 210
  *	0.66f	24/1/04	-s: Allow clamd server name as well as IPaddress
211
+ *	0.66g	25/1/04 Corrected usage message
212
+ *			Started to honour --debug
213
+ *			Dump core on LINUX if CL_DEBUG set
214
+ *			Support multiple servers separated by colons
211 215
  *
212 216
  * Change History:
213 217
  * $Log: clamav-milter.c,v $
218
+ * Revision 1.38  2004/01/25 14:23:51  nigelhorne
219
+ * Support multiple clamd servers
220
+ *
214 221
  * Revision 1.37  2004/01/24 18:09:39  nigelhorne
215 222
  * Allow clamd server name as well as IPaddress in -s option
216 223
  *
... ...
@@ -307,15 +314,17 @@
307 307
  * Revision 1.6  2003/09/28 16:37:23  nigelhorne
308 308
  * Added -f flag use MaxThreads if --max-children not set
309 309
  */
310
-static	char	const	rcsid[] = "$Id: clamav-milter.c,v 1.37 2004/01/24 18:09:39 nigelhorne Exp $";
310
+static	char	const	rcsid[] = "$Id: clamav-milter.c,v 1.38 2004/01/25 14:23:51 nigelhorne Exp $";
311 311
 
312
-#define	CM_VERSION	"0.66f"
312
+#define	CM_VERSION	"0.66g"
313 313
 
314 314
 /*#define	CONFDIR	"/usr/local/etc"*/
315 315
 
316 316
 #include "defaults.h"
317 317
 #include "cfgfile.h"
318 318
 #include "../target.h"
319
+#include "str.h"
320
+#include "others.h"
319 321
 
320 322
 #ifndef	CL_DEBUG
321 323
 #define	NDEBUG
... ...
@@ -350,6 +359,10 @@ static	char	const	rcsid[] = "$Id: clamav-milter.c,v 1.37 2004/01/24 18:09:39 nig
350 350
 #include <grp.h>
351 351
 #include <netdb.h>
352 352
 
353
+#if defined(CL_DEBUG) && defined(C_LINUX)
354
+#include <sys/resource.h>
355
+#endif
356
+
353 357
 #define _GNU_SOURCE
354 358
 #include "getopt.h"
355 359
 
... ...
@@ -366,7 +379,6 @@ static	char	const	rcsid[] = "$Id: clamav-milter.c,v 1.37 2004/01/24 18:09:39 nig
366 366
  * TODO: bounce message should optionally be read from a file
367 367
  * TODO: Support ThreadTimeout, LogTime and Logfile from the conf
368 368
  *	 file
369
- * TODO: Allow more than one clamdscan server to be given
370 369
  */
371 370
 
372 371
 /*
... ...
@@ -386,7 +398,8 @@ struct	privdata {
386 386
 	size_t	bodyLen;	/* number of bytes in body */
387 387
 };
388 388
 
389
-static	int		pingServer(void);
389
+static	int		pingServer(int serverNumber);
390
+static	int		findServer(void);
390 391
 static	sfsistat	clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr);
391 392
 static	sfsistat	clamfi_envfrom(SMFICTX *ctx, char **argv);
392 393
 static	sfsistat	clamfi_envrcpt(SMFICTX *ctx, char **argv);
... ...
@@ -465,13 +478,14 @@ static	pthread_mutex_t	n_children_mutex = PTHREAD_MUTEX_INITIALIZER;
465 465
 static	pthread_cond_t	n_children_cond = PTHREAD_COND_INITIALIZER;
466 466
 static	unsigned	int	n_children = 0;
467 467
 static	unsigned	int	max_children = 0;
468
-static	int	use_syslog = 0;
468
+short	use_syslog = 0;
469 469
 static	int	logVerbose = 0;
470 470
 static	struct	cfgstruct	*copt;
471 471
 static	const	char	*localSocket;
472 472
 static	in_port_t	tcpSocket;
473
-static	const	char	*serverHostName = "127.0.0.1";
474
-static	long	serverIP = -1L;	/* IPv4 only */
473
+static	const	char	*serverHostNames = "127.0.0.1";
474
+static	long	*serverIPs;	/* IPv4 only */
475
+static	int	numServers;	/* numer of elements in serverIPs */
475 476
 static	const	char	*postmaster = "postmaster";
476 477
 
477 478
 /*
... ...
@@ -492,6 +506,7 @@ help(void)
492 492
 
493 493
 	puts("\t--bounce\t\t-b\tSend a failure message to the sender.");
494 494
 	puts("\t--config-file=FILE\t-c FILE\tRead configuration from FILE.");
495
+	puts("\t--debug\t\t-D\tPrint debug messages.");
495 496
 	puts("\t--dont-scan-on-error\t-d\tPass e-mails through unscanned if a system error occurs.");
496 497
 	puts("\t--force-scan\t\t-f\tForce scan all messages (overrides (-o and -l).");
497 498
 	puts("\t--help\t\t\t-h\tThis message.");
... ...
@@ -503,7 +518,7 @@ help(void)
503 503
 	puts("\t--quiet\t\t\t-q\tDon't send e-mail notifications of interceptions.");
504 504
 	puts("\t--quarantine=USER\t-Q EMAIL\tQuanrantine e-mail account.");
505 505
 	puts("\t--quarantine-dir=DIR\t-U DIR\tDirectory to store infected emails.");
506
-	puts("\t--server=ADDRESS\t-s ADDR\tHostname/IP address of server running clamd (when using TCPsocket).");
506
+	puts("\t--server=SERVER\t-s SERVER\tHostname/IP address of server(s) running clamd (when using TCPsocket).");
507 507
 	puts("\t--sign\t\t\t-S\tAdd a hard-coded signature to each scanned message.");
508 508
 	puts("\t--signature-file\t-F\tLocation of signature file.");
509 509
 	puts("\t--version\t\t-V\tPrint the version number of this software.");
... ...
@@ -536,6 +551,13 @@ main(int argc, char **argv)
536 536
 		clamfi_close, /* connection cleanup callback */
537 537
 	};
538 538
 
539
+#if defined(CL_DEBUG) && defined(C_LINUX)
540
+	struct rlimit rlim;
541
+
542
+	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
543
+	if(setrlimit(RLIMIT_CORE, &rlim) < 0)
544
+		perror("setrlimit");
545
+#endif
539 546
 	/*
540 547
 	 * Temporarily enter guessed value into clamav_version, will
541 548
 	 * be overwritten later by the value returned by clamd
... ...
@@ -547,9 +569,9 @@ main(int argc, char **argv)
547 547
 	for(;;) {
548 548
 		int opt_index = 0;
549 549
 #ifdef	CL_DEBUG
550
-		const char *args = "bc:fF:lm:nop:PqQ:dhs:SU:Vx:";
550
+		const char *args = "bc:DfF:lm:nop:PqQ:dhs:SU:Vx:";
551 551
 #else
552
-		const char *args = "bc:fF:lm:nop:PqQ:dhs:SU:V";
552
+		const char *args = "bc:DfF:lm:nop:PqQ:dhs:SU:V";
553 553
 #endif
554 554
 
555 555
 		static struct option long_options[] = {
... ...
@@ -563,6 +585,9 @@ main(int argc, char **argv)
563 563
 				"dont-scan-on-error", 0, NULL, 'd'
564 564
 			},
565 565
 			{
566
+				"debug", 0, NULL, 'D'
567
+			},
568
+			{
566 569
 				"force-scan", 0, NULL, 'f'
567 570
 			},
568 571
 			{
... ...
@@ -634,6 +659,9 @@ main(int argc, char **argv)
634 634
 			case 'd':	/* don't scan on error */
635 635
 				cl_error = SMFIS_ACCEPT;
636 636
 				break;
637
+			case 'D':	/* enable debug messages */
638
+				cl_debug();
639
+				break;
637 640
 			case 'f':	/* force the scan */
638 641
 				fflag++;
639 642
 				break;
... ...
@@ -667,7 +695,7 @@ main(int argc, char **argv)
667 667
 				smfilter.xxfi_flags |= SMFIF_CHGHDRS|SMFIF_ADDRCPT|SMFIF_DELRCPT;
668 668
 				break;
669 669
 			case 's':	/* server running clamd */
670
-				serverHostName = optarg;
670
+				serverHostNames = optarg;
671 671
 				break;
672 672
 			case 'F':	/* signature file */
673 673
 				sigFilename = optarg;
... ...
@@ -690,9 +718,9 @@ main(int argc, char **argv)
690 690
 #endif
691 691
 			default:
692 692
 #ifdef	CL_DEBUG
693
-				fprintf(stderr, "Usage: %s [-b] [-c FILE] [-F FILE] [--max-children=num] [-l] [-o] [-p address] [-P] [-q] [-Q USER] [-S] [-x#] [-U PATH] socket-addr\n", argv[0]);
693
+				fprintf(stderr, "Usage: %s [-b] [-c FILE] [-F FILE] [--max-children=num] [-l] [-o] [-p address] [-P] [-q] [-Q USER] [-s SERVER] [-S] [-x#] [-U PATH] socket-addr\n", argv[0]);
694 694
 #else
695
-				fprintf(stderr, "Usage: %s [-b] [-c FILE] [-F FILE] [--max-children=num] [-l] [-o] [-p address] [-P] [-q] [-Q USER] [-S] [-U PATH] socket-addr\n", argv[0]);
695
+				fprintf(stderr, "Usage: %s [-b] [-c FILE] [-F FILE] [--max-children=num] [-l] [-o] [-p address] [-P] [-q] [-Q USER] [-s SERVER] [-S] [-U PATH] socket-addr\n", argv[0]);
696 696
 #endif
697 697
 				return EX_USAGE;
698 698
 		}
... ...
@@ -783,7 +811,7 @@ main(int argc, char **argv)
783 783
 		 * TODO: check --server hasn't been set
784 784
 		 */
785 785
 		localSocket = cpt->strarg;
786
-		if(!pingServer()) {
786
+		if(!pingServer(-1)) {
787 787
 			fprintf(stderr, "Can't talk to clamd server via %s\n",
788 788
 				localSocket);
789 789
 			fprintf(stderr, "Check your entry for LocalSocket in %s\n",
... ...
@@ -791,7 +819,13 @@ main(int argc, char **argv)
791 791
 			return EX_CONFIG;
792 792
 		}
793 793
 		umask(022);
794
+
795
+		serverIPs = (long *)cli_malloc(sizeof(long));
796
+		serverIPs[0] = inet_addr("127.0.0.1");
797
+
794 798
 	} else if((cpt = cfgopt(copt, "TCPSocket")) != NULL) {
799
+		int i;
800
+
795 801
 		/*
796 802
 		 * TCPSocket is in fact a port number not a full socket
797 803
 		 */
... ...
@@ -800,30 +834,53 @@ main(int argc, char **argv)
800 800
 			return EX_CONFIG;
801 801
 		}
802 802
 
803
+		tcpSocket = cpt->numarg;
804
+
803 805
 		/*
804
-		 * Translate server's name to IP address
806
+		 * cli_strtok's fieldno counts from 0
805 807
 		 */
806
-		serverIP = inet_addr(serverHostName);
807
-		if(serverIP == -1L) {
808
-			const struct hostent *h = gethostbyname(serverHostName);
808
+		for(;;) {
809
+			char *hostname;
809 810
 
810
-			if(h == NULL) {
811
-				fprintf(stderr, "%s: Unknown host %s\n",
812
-					argv[0], serverHostName);
813
-				return EX_USAGE;
814
-			}
815
-
816
-			memcpy((char *)&serverIP, h->h_addr, sizeof(serverIP));
811
+			hostname = cli_strtok(serverHostNames, numServers, ":");
812
+			if(hostname == NULL)
813
+				break;
814
+			numServers++;
815
+			free(hostname);
817 816
 		}
818 817
 
819
-		tcpSocket = cpt->numarg;
818
+		cli_dbgmsg("numServers: %d\n", numServers);
820 819
 
821
-		if(!pingServer()) {
822
-			fprintf(stderr, "Can't talk to clamd server at %s on port %d\n",
823
-				serverHostName, tcpSocket);
824
-			fprintf(stderr, "Check your entry for TCPSocket in %s\n",
825
-				cfgfile);
826
-			return EX_CONFIG;
820
+		serverIPs = (long *)cli_malloc(numServers * sizeof(long));
821
+
822
+		for(i = 0; i < numServers; i++) {
823
+			char *hostname;
824
+
825
+			/*
826
+			 * Translate server's name to IP address
827
+			 */
828
+			hostname = cli_strtok(serverHostNames, i, ":");
829
+			serverIPs[i] = inet_addr(hostname);
830
+			if(serverIPs[i] == -1L) {
831
+				const struct hostent *h = gethostbyname(hostname);
832
+
833
+				if(h == NULL) {
834
+					fprintf(stderr, "%s: Unknown host %s\n",
835
+						argv[0], hostname);
836
+					return EX_USAGE;
837
+				}
838
+
839
+				memcpy((char *)&serverIPs[i], h->h_addr, sizeof(serverIPs[i]));
840
+			}
841
+
842
+			if(!pingServer(i)) {
843
+				fprintf(stderr, "Can't talk to clamd server %s on port %d\n",
844
+					hostname, tcpSocket);
845
+				fprintf(stderr, "Check your entry for TCPSocket in %s\n",
846
+					cfgfile);
847
+				return EX_CONFIG;
848
+			}
849
+			free(hostname);
827 850
 		}
828 851
 	} else {
829 852
 		fprintf(stderr, "%s: You must select server type (local/TCP) in %s\n",
... ...
@@ -922,9 +979,11 @@ main(int argc, char **argv)
922 922
 /*
923 923
  * Verify that the server is where we think it is
924 924
  * Returns true or false
925
+ *
926
+ * serverNumber counts from 0
925 927
  */
926 928
 static int
927
-pingServer(void)
929
+pingServer(int serverNumber)
928 930
 {
929 931
 	char *ptr;
930 932
 	int sock, nbytes;
... ...
@@ -952,9 +1011,10 @@ pingServer(void)
952 952
 		server.sin_family = AF_INET;
953 953
 		server.sin_port = htons(tcpSocket);
954 954
 		
955
-		assert(serverIP != -1L);
955
+		assert(serverIP != NULL);
956
+		assert(serverIPs[0] != -1L);
956 957
 
957
-		server.sin_addr.s_addr = serverIP;
958
+		server.sin_addr.s_addr = serverIPs[serverNumber];
958 959
 
959 960
 		if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
960 961
 			perror("socket");
... ...
@@ -1003,6 +1063,10 @@ pingServer(void)
1003 1003
 
1004 1004
 	/*
1005 1005
 	 * No real validation is done here
1006
+	 *
1007
+	 * TODO: When connecting to more than one server, give a warning
1008
+	 *	if they're running different versions, or if the virus DBs
1009
+	 *	are out of date
1006 1010
 	 */
1007 1011
 	snprintf(clamav_version, sizeof(clamav_version),
1008 1012
 		"ClamAV version '%s', clamav-milter version '%s'",
... ...
@@ -1011,6 +1075,109 @@ pingServer(void)
1011 1011
 	return 1;
1012 1012
 }
1013 1013
 
1014
+/*
1015
+ * Find the best server to connect to. No intelligence to this, if one
1016
+ * of the servers goes down it'll be silently ignored, which is probably
1017
+ * ok. It is best to weight the order of the servers from most wanted to
1018
+ * least wanted
1019
+ *
1020
+ * Return value is from 0 - index into serverIPs
1021
+ */
1022
+static int
1023
+findServer(void)
1024
+{
1025
+	struct sockaddr_in *servers, *server;
1026
+	int *socks, maxsock = 0, i;
1027
+	fd_set rfds;
1028
+	struct timeval tv;
1029
+	int retval;
1030
+
1031
+	assert(tcpSocket != 0);
1032
+	assert(numServers > 0);
1033
+
1034
+	if(numServers == 1)
1035
+		return 0;
1036
+
1037
+	servers = (struct sockaddr_in *)cli_calloc(numServers, sizeof(struct sockaddr_in));
1038
+	socks = (int *)cli_malloc(numServers * sizeof(int));
1039
+
1040
+	FD_ZERO(&rfds);
1041
+
1042
+	for(i = 0, server = servers; i < numServers; i++, server++) {
1043
+		int sock;
1044
+
1045
+		server->sin_family = AF_INET;
1046
+		server->sin_port = htons(tcpSocket);
1047
+		server->sin_addr.s_addr = serverIPs[i];
1048
+
1049
+		sock = socks[i] = socket(AF_INET, SOCK_STREAM, 0);
1050
+		if(sock < 0) {
1051
+			perror("socket");
1052
+			do
1053
+				if(socks[i] >= 0)
1054
+					close(socks[i]);
1055
+			while(--i >= 0);
1056
+			free(socks);
1057
+			free(servers);
1058
+			return 0;	/* Use the first server on failure */
1059
+		}
1060
+
1061
+		if((connect(sock, (struct sockaddr *)server, sizeof(struct sockaddr)) < 0) ||
1062
+		   (send(sock, "PING\n", 5, 0) < 5)) {
1063
+			cli_errmsg("findServer: Check server %d - it may be down\n", i);
1064
+			if(use_syslog)
1065
+				syslog(LOG_WARNING, "findServer: Check server %d - it may be down", i);
1066
+			socks[i] = -1;
1067
+			continue;
1068
+		}
1069
+
1070
+		shutdown(sock, SHUT_WR);
1071
+
1072
+		FD_SET(sock, &rfds);
1073
+		if(sock > maxsock)
1074
+			maxsock = sock;
1075
+	}
1076
+
1077
+	free(servers);
1078
+
1079
+	tv.tv_sec = threadtimeout;
1080
+	tv.tv_usec = 0;
1081
+	
1082
+	retval = select(maxsock, &rfds, NULL, NULL, &tv);
1083
+	if(retval < 0)
1084
+		perror("select");
1085
+
1086
+	for(i = 0; i < numServers; i++)
1087
+		if(socks[i] >= 0)
1088
+			close(socks[i]);
1089
+
1090
+	if(retval == 0) {
1091
+		free(socks);
1092
+		cli_dbgmsg("findServer: No response from any server\n");
1093
+		if(use_syslog)
1094
+			syslog(LOG_WARNING, "findServer: No response from any server");
1095
+		return 0;
1096
+	} else if(retval < 0) {
1097
+		free(socks);
1098
+		if(use_syslog)
1099
+			syslog(LOG_ERR, "findServer: select failed\n");
1100
+		return 0;
1101
+	}
1102
+
1103
+	for(i = 0; i < numServers; i++)
1104
+		if(FD_ISSET(socks[i], &rfds)) {
1105
+			free(socks);
1106
+			cli_dbgmsg("findServer: using server %d", i);
1107
+			return i;
1108
+		}
1109
+
1110
+	free(socks);
1111
+	cli_dbgmsg("findServer: No response from any server\n");
1112
+	if(use_syslog)
1113
+		syslog(LOG_WARNING, "findServer: No response from any server");
1114
+	return 0;
1115
+}
1116
+
1014 1117
 static sfsistat
1015 1118
 clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
1016 1119
 {
... ...
@@ -1109,7 +1276,7 @@ clamfi_envfrom(SMFICTX *ctx, char **argv)
1109 1109
 	struct privdata *privdata;
1110 1110
 	struct sockaddr_in reply;
1111 1111
 	unsigned short port;
1112
-	int nbytes, rc;
1112
+	int nbytes, rc, freeServer;
1113 1113
 	char buf[64];
1114 1114
 
1115 1115
 	if(logVerbose)
... ...
@@ -1228,6 +1395,7 @@ clamfi_envfrom(SMFICTX *ctx, char **argv)
1228 1228
 				perror(localSocket);
1229 1229
 				return cl_error;
1230 1230
 			}
1231
+			freeServer = 0;
1231 1232
 		} else {
1232 1233
 			struct sockaddr_in server;
1233 1234
 
... ...
@@ -1235,9 +1403,13 @@ clamfi_envfrom(SMFICTX *ctx, char **argv)
1235 1235
 			server.sin_family = AF_INET;
1236 1236
 			server.sin_port = htons(tcpSocket);
1237 1237
 
1238
-			assert(serverIP != -1L);
1238
+			assert(serverIP != NULL);
1239
+
1240
+			freeServer = findServer();
1241
+			if(freeServer < 0)
1242
+				return cl_error;
1239 1243
 
1240
-			server.sin_addr.s_addr = serverIP;
1244
+			server.sin_addr.s_addr = serverIPs[freeServer];
1241 1245
 
1242 1246
 			if((privdata->cmdSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
1243 1247
 				perror("socket");
... ...
@@ -1307,9 +1479,9 @@ clamfi_envfrom(SMFICTX *ctx, char **argv)
1307 1307
 		reply.sin_family = AF_INET;
1308 1308
 		reply.sin_port = ntohs(port);
1309 1309
 
1310
-		assert(serverIP != -1L);
1310
+		assert(serverIP != NULL);
1311 1311
 
1312
-		reply.sin_addr.s_addr = serverIP;
1312
+		reply.sin_addr.s_addr = serverIPs[freeServer];
1313 1313
 
1314 1314
 #ifdef	CL_DEBUG
1315 1315
 		if(debug_level >= 4)