Browse code

apply timeout patch from Everton da Silva Marques

git-svn: trunk@2216

Tomasz Kojm authored on 2006/08/28 02:59:01
Showing 9 changed files
... ...
@@ -1,3 +1,8 @@
1
+Sun Aug 27 19:48:17 CEST 2006 (tk)
2
+----------------------------------
3
+  * freshclam: apply timeout patch from Everton da Silva Marques
4
+	       <everton*lab.ipaccess.diveo.net.br>
5
+
1 6
 Sun Aug 27 10:52:55 BST 2006 (njh)
2 7
 ----------------------------------
3 8
   * libclamav/blob.c:	Don't use % in filenames on Windows, since cmd.exe
... ...
@@ -121,6 +121,16 @@ Default: disabled
121 121
 Execute this command after a database update has failed.
122 122
 .br 
123 123
 Default: disabled
124
+.TP
125
+\fBConnectTimeout\fR
126
+Timeout in seconds when connecting to database server.
127
+.br 
128
+Default: 10
129
+.TP
130
+\fBReceiveTimeout\fR
131
+Timeout in seconds when reading from database server.
132
+.br 
133
+Default: 30
124 134
 .SH "NOTE"
125 135
 While not reasonable, any configuration option from clamd.conf(5) may be given.
126 136
 .SH "FILES"
... ...
@@ -111,3 +111,11 @@ DatabaseMirror database.clamav.net
111 111
 # Enable debug messages in libclamav.
112 112
 # Default: no
113 113
 #Debug yes
114
+
115
+# Timeout in seconds when connecting to database server.
116
+# Default: 10
117
+#ConnectTimeout 30
118
+
119
+# Timeout in seconds when reading from database server.
120
+# Default: 30
121
+#ReceiveTimeout 60
... ...
@@ -44,7 +44,9 @@ freshclam_SOURCES = \
44 44
     dns.c \
45 45
     dns.h \
46 46
     execute.c \
47
-    execute.h
47
+    execute.h \
48
+    nonblock.c \
49
+    nonblock.h
48 50
 
49 51
 DEFS = @DEFS@ -DCL_NOTHREADS
50 52
 INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/shared -I$(top_srcdir)/libclamav
... ...
@@ -74,7 +74,7 @@ am_freshclam_OBJECTS = output.$(OBJEXT) cfgparser.$(OBJEXT) \
74 74
 	getopt.$(OBJEXT) memory.$(OBJEXT) misc.$(OBJEXT) \
75 75
 	options.$(OBJEXT) cdiff.$(OBJEXT) freshclam.$(OBJEXT) \
76 76
 	manager.$(OBJEXT) notify.$(OBJEXT) dns.$(OBJEXT) \
77
-	execute.$(OBJEXT)
77
+	execute.$(OBJEXT) nonblock.$(OBJEXT)
78 78
 freshclam_OBJECTS = $(am_freshclam_OBJECTS)
79 79
 freshclam_LDADD = $(LDADD)
80 80
 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
... ...
@@ -232,7 +232,9 @@ freshclam_SOURCES = \
232 232
     dns.c \
233 233
     dns.h \
234 234
     execute.c \
235
-    execute.h
235
+    execute.h \
236
+    nonblock.c \
237
+    nonblock.h
236 238
 
237 239
 INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/shared -I$(top_srcdir)/libclamav
238 240
 all: all-am
... ...
@@ -315,6 +317,7 @@ distclean-compile:
315 315
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manager.Po@am__quote@
316 316
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Po@am__quote@
317 317
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
318
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nonblock.Po@am__quote@
318 319
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify.Po@am__quote@
319 320
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
320 321
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
... ...
@@ -46,6 +46,7 @@
46 46
 #include "notify.h"
47 47
 #include "dns.h"
48 48
 #include "execute.h"
49
+#include "nonblock.h"
49 50
 
50 51
 #include "shared/options.h"
51 52
 #include "shared/cfgparser.h"
... ...
@@ -63,7 +64,7 @@
63 63
 #endif
64 64
 
65 65
 
66
-static int wwwconnect(const char *server, const char *proxy, int pport, char *ip, const char *localip)
66
+static int wwwconnect(const char *server, const char *proxy, int pport, char *ip, const char *localip, int ctimeout)
67 67
 {
68 68
 	int socketfd = -1, port, i;
69 69
 	struct sockaddr_in name;
... ...
@@ -195,7 +196,7 @@ static int wwwconnect(const char *server, const char *proxy, int pport, char *ip
195 195
 	name.sin_addr = *((struct in_addr *) host->h_addr_list[i]);
196 196
 	name.sin_port = htons(port);
197 197
 
198
-	if(connect(socketfd, (struct sockaddr *) &name, sizeof(struct sockaddr_in)) == -1) {
198
+	if(wait_connect(socketfd, (struct sockaddr *) &name, sizeof(struct sockaddr_in), ctimeout) == -1) {
199 199
 	    logg("Can't connect to port %d of host %s (IP: %s)\n", port, hostpt, ipaddr);
200 200
 	    continue;
201 201
 	} else {
... ...
@@ -283,7 +284,7 @@ static char *proxyauth(const char *user, const char *pass)
283 283
     return auth;
284 284
 }
285 285
 
286
-static struct cl_cvd *remote_cvdhead(const char *file, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int *ims)
286
+static struct cl_cvd *remote_cvdhead(const char *file, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int *ims, int ctimeout, int rtimeout)
287 287
 {
288 288
 	char cmd[512], head[513], buffer[FILEBUFF], ipaddr[16], *ch, *tmp;
289 289
 	int bread, cnt, sd;
... ...
@@ -343,9 +344,9 @@ static struct cl_cvd *remote_cvdhead(const char *file, const char *hostname, cha
343 343
     memset(ipaddr, 0, sizeof(ipaddr));
344 344
 
345 345
     if(ip[0]) /* use ip to connect */
346
-	sd = wwwconnect(ip, proxy, port, ipaddr, localip);
346
+	sd = wwwconnect(ip, proxy, port, ipaddr, localip, ctimeout);
347 347
     else
348
-	sd = wwwconnect(hostname, proxy, port, ipaddr, localip);
348
+	sd = wwwconnect(hostname, proxy, port, ipaddr, localip, ctimeout);
349 349
 
350 350
     if(sd < 0) {
351 351
 	return NULL;
... ...
@@ -365,7 +366,7 @@ static struct cl_cvd *remote_cvdhead(const char *file, const char *hostname, cha
365 365
 
366 366
     tmp = buffer;
367 367
     cnt = FILEBUFF;
368
-    while((bread = recv(sd, tmp, cnt, 0)) > 0) {
368
+    while((bread = wait_recv(sd, tmp, cnt, 0, rtimeout)) > 0) {
369 369
 	tmp += bread;
370 370
 	cnt -= bread;
371 371
 	if(cnt <= 0)
... ...
@@ -433,7 +434,7 @@ static struct cl_cvd *remote_cvdhead(const char *file, const char *hostname, cha
433 433
     return cvd;
434 434
 }
435 435
 
436
-static int getfile(const char *srcfile, const char *destfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas)
436
+static int getfile(const char *srcfile, const char *destfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int ctimeout, int rtimeout)
437 437
 {
438 438
 	char cmd[512], buffer[FILEBUFF], *ch;
439 439
 	int bread, fd, totalsize = 0,  rot = 0, totaldownloaded = 0,
... ...
@@ -476,9 +477,9 @@ static int getfile(const char *srcfile, const char *destfile, const char *hostna
476 476
     memset(ipaddr, 0, sizeof(ipaddr));
477 477
 
478 478
     if(ip[0]) /* use ip to connect */
479
-	sd = wwwconnect(ip, proxy, port, ipaddr, localip);
479
+	sd = wwwconnect(ip, proxy, port, ipaddr, localip, ctimeout);
480 480
     else
481
-	sd = wwwconnect(hostname, proxy, port, ipaddr, localip);
481
+	sd = wwwconnect(hostname, proxy, port, ipaddr, localip, ctimeout);
482 482
 
483 483
     if(sd < 0) {
484 484
 	return 52;
... ...
@@ -505,7 +506,7 @@ static int getfile(const char *srcfile, const char *destfile, const char *hostna
505 505
     i = 0;
506 506
     while(1) {
507 507
 	/* recv one byte at a time, until we reach \r\n\r\n */
508
-	if((i >= sizeof(buffer) - 1) || recv(sd, buffer + i, 1, 0) == -1) {
508
+	if((i >= sizeof(buffer) - 1) || wait_recv(sd, buffer + i, 1, 0, rtimeout) == -1) {
509 509
 	    logg("!getfile: Error while reading database from %s\n", hostname);
510 510
 	    return 52;
511 511
 	}
... ...
@@ -556,7 +557,7 @@ static int getfile(const char *srcfile, const char *destfile, const char *hostna
556 556
 	return 57;
557 557
     }
558 558
 
559
-    while((bread = read(sd, buffer, FILEBUFF))) {
559
+    while((bread = wait_recv(sd, buffer, FILEBUFF, 0, rtimeout)) > 0) {
560 560
         if(write(fd, buffer, bread) != bread) {
561 561
 	    logg("getfile: Can't write %d bytes to %s\n", bread, destfile);
562 562
 	    unlink(destfile);
... ...
@@ -589,7 +590,7 @@ static int getfile(const char *srcfile, const char *destfile, const char *hostna
589 589
     return 0;
590 590
 }
591 591
 
592
-static int getcvd(const char *dbfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int nodb, int newver)
592
+static int getcvd(const char *dbfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int nodb, int newver, int ctimeout, int rtimeout)
593 593
 {
594 594
 	char *tempname;
595 595
 	struct cl_cvd *cvd;
... ...
@@ -599,7 +600,7 @@ static int getcvd(const char *dbfile, const char *hostname, char *ip, const char
599 599
     tempname = cli_gentemp(".");
600 600
 
601 601
     logg("*Retrieving http://%s/%s\n", hostname, dbfile);
602
-    if((ret = getfile(dbfile, tempname, hostname, ip, localip, proxy, port, user, pass, uas))) {
602
+    if((ret = getfile(dbfile, tempname, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout))) {
603 603
         logg("!Can't download %s from %s (IP: %s)\n", dbfile, hostname, ip);
604 604
         unlink(tempname);
605 605
         free(tempname);
... ...
@@ -679,7 +680,7 @@ static int chdir_inc(const char *dbname)
679 679
     return 0;
680 680
 }
681 681
 
682
-static int getpatch(const char *dbname, int version, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas)
682
+static int getpatch(const char *dbname, int version, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int ctimeout, int rtimeout)
683 683
 {
684 684
 	char *tempname, patch[32], olddir[512];
685 685
 	int ret, fd;
... ...
@@ -697,7 +698,7 @@ static int getpatch(const char *dbname, int version, const char *hostname, char
697 697
     snprintf(patch, sizeof(patch), "%s-%d.cdiff", dbname, version);
698 698
 
699 699
     logg("*Retrieving http://%s/%s\n", hostname, patch);
700
-    if((ret = getfile(patch, tempname, hostname, ip, localip, proxy, port, user, pass, uas))) {
700
+    if((ret = getfile(patch, tempname, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout))) {
701 701
         logg("!getpatch: Can't download %s from %s (IP: %s)\n", patch, hostname, ip);
702 702
         unlink(tempname);
703 703
         free(tempname);
... ...
@@ -758,6 +759,7 @@ int updatedb(const char *dbname, const char *hostname, char *ip, int *signo, con
758 758
 	const char *proxy = NULL, *user = NULL, *pass = NULL, *uas = NULL;
759 759
 	int flevel = cl_retflevel();
760 760
 	struct stat sb;
761
+	int ctimeout, rtimeout;
761 762
 
762 763
 
763 764
     snprintf(dbfile, sizeof(dbfile), "%s.cvd", dbname);
... ...
@@ -835,10 +837,12 @@ int updatedb(const char *dbname, const char *hostname, char *ip, int *signo, con
835 835
     if((cpt = cfgopt(copt, "HTTPUserAgent"))->enabled)
836 836
 	uas = cpt->strarg;
837 837
 
838
+    ctimeout = cfgopt(copt, "ConnectTimeout")->numarg;
839
+    rtimeout = cfgopt(copt, "ReceiveTimeout")->numarg;
838 840
 
839 841
     if(!nodb && newver == -1) {
840 842
 
841
-	remote = remote_cvdhead(dbfile, hostname, ip, localip, proxy, port, user, pass, uas, &ims);
843
+	remote = remote_cvdhead(dbfile, hostname, ip, localip, proxy, port, user, pass, uas, &ims, ctimeout, rtimeout);
842 844
 
843 845
 	if(!nodb && !ims) {
844 846
 	    logg("%s is up to date (version: %d, sigs: %d, f-level: %d, builder: %s)\n", dbfile, current->version, current->sigs, current->fl, current->builder);
... ...
@@ -897,7 +901,7 @@ int updatedb(const char *dbname, const char *hostname, char *ip, int *signo, con
897 897
     */
898 898
 
899 899
     if(nodb) {
900
-	ret = getcvd(dbfile, hostname, ip, localip, proxy, port, user, pass, uas, nodb, newver);
900
+	ret = getcvd(dbfile, hostname, ip, localip, proxy, port, user, pass, uas, nodb, newver, ctimeout, rtimeout);
901 901
 	if(ret)
902 902
 	    return ret;
903 903
 
... ...
@@ -905,7 +909,7 @@ int updatedb(const char *dbname, const char *hostname, char *ip, int *signo, con
905 905
 	ret = 0;
906 906
 
907 907
 	for(i = currver + 1; i <= newver; i++) {
908
-	    ret = getpatch(dbname, i, hostname, ip, localip, proxy, port, user, pass, uas);
908
+	    ret = getpatch(dbname, i, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout);
909 909
 	    if(ret) {
910 910
 		logg("^Removing incremental directory %s\n", dbinc);
911 911
 		rmdirs(dbinc);
... ...
@@ -916,7 +920,7 @@ int updatedb(const char *dbname, const char *hostname, char *ip, int *signo, con
916 916
 	if(ret) {
917 917
 	    logg("^Incremental update failed, downloading complete database\n");
918 918
 
919
-	    ret = getcvd(dbfile, hostname, ip, localip, proxy, port, user, pass, uas, 1, newver);
919
+	    ret = getcvd(dbfile, hostname, ip, localip, proxy, port, user, pass, uas, 1, newver, ctimeout, rtimeout);
920 920
 	    if(ret)
921 921
 		return ret;
922 922
 	} else {
923 923
new file mode 100644
... ...
@@ -0,0 +1,302 @@
0
+/*
1
+ *  Copyright 2006 Everton da Silva Marques <everton.marques@gmail.com>
2
+ *
3
+ *  This program is free software; you can redistribute it and/or modify
4
+ *  it under the terms of the GNU General Public License as published by
5
+ *  the Free Software Foundation; either version 2 of the License, or
6
+ *  (at your option) any later version.
7
+ *
8
+ *  This program is distributed in the hope that it will be useful,
9
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+ *  GNU General Public License for more details.
12
+ *
13
+ *  You should have received a copy of the GNU General Public License
14
+ *  along with this program; if not, write to the Free Software
15
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
+ */
17
+
18
+#include <stdio.h>
19
+#include <stdlib.h>
20
+#include <unistd.h>
21
+#include <string.h>
22
+#include <ctype.h>
23
+#include <netinet/in.h>
24
+#include <netdb.h>
25
+#include <sys/types.h>
26
+#include <sys/socket.h>
27
+#include <sys/time.h>
28
+#include <time.h>
29
+#include <fcntl.h>
30
+#include <sys/stat.h>
31
+#include <clamav.h>
32
+#include <errno.h>
33
+
34
+#include "nonblock.h"
35
+#include "output.h"
36
+
37
+#ifndef timercmp
38
+# define timercmp(a, b, cmp)          \
39
+  (((a)->tv_sec == (b)->tv_sec) ?     \
40
+   ((a)->tv_usec cmp (b)->tv_usec) :  \
41
+   ((a)->tv_sec cmp (b)->tv_sec))
42
+#endif /* timercmp */
43
+
44
+#ifndef timersub
45
+# define timersub(a, b, result)                       \
46
+  do {                                                \
47
+    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;     \
48
+    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;  \
49
+    if ((result)->tv_usec < 0) {                      \
50
+      --(result)->tv_sec;                             \
51
+      (result)->tv_usec += 1000000;                   \
52
+    }                                                 \
53
+  } while (0)
54
+#endif /* timersub */
55
+
56
+#define NONBLOCK_SELECT_MAX_FAILURES 3
57
+#define NONBLOCK_MAX_BOGUS_LOOPS     10
58
+#undef  NONBLOCK_DEBUG
59
+
60
+static int connect_error(int sock)
61
+{
62
+	int optval;
63
+	int optlen;
64
+
65
+	optlen = sizeof(optval);
66
+	getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen);
67
+
68
+	if (optval) {
69
+		logg("%s %s: getsockopt(SO_ERROR): fd=%d error=%d: %s\n",
70
+		     __FILE__, __PRETTY_FUNCTION__,
71
+		     sock, optval, strerror(optval));
72
+	}
73
+
74
+	return optval ? -1 : 0;
75
+}
76
+
77
+static int nonblock_connect(int sock, const struct sockaddr *addr, socklen_t addrlen, int secs)
78
+{
79
+	/* Max. of unexpected select() failures */
80
+	int select_failures = NONBLOCK_SELECT_MAX_FAILURES;
81
+	/* Max. of useless loops */
82
+	int bogus_loops = NONBLOCK_MAX_BOGUS_LOOPS;
83
+	struct timeval timeout;  /* When we should time out */
84
+	int numfd;               /* Highest fdset fd plus 1 */
85
+
86
+	/* Calculate into 'timeout' when we should time out */
87
+	gettimeofday(&timeout, 0);
88
+	timeout.tv_sec += secs;
89
+
90
+	/* Launch (possibly) non-blocking connect() request */
91
+	if (connect(sock, addr, addrlen)) {
92
+		int e = errno;
93
+#ifdef NONBLOCK_DEBUG
94
+		logg("DEBUG %s %s: connect(): fd=%d errno=%d: %s\n",
95
+		     __FILE__, __PRETTY_FUNCTION__,
96
+		     sock, e, strerror(e));
97
+#endif
98
+		switch (e) {
99
+		case EALREADY:
100
+		case EINPROGRESS:
101
+			break; /* wait for connection */
102
+		case EISCONN:
103
+			return 0; /* connected */
104
+		default:
105
+			logg("%s %s: connect(): fd=%d errno=%d: %s\n",
106
+			     __FILE__, __PRETTY_FUNCTION__,
107
+			     sock, e, strerror(e));
108
+			return -1; /* failed */
109
+		}
110
+	}
111
+	else {
112
+		return connect_error(sock);
113
+	}
114
+
115
+	numfd = sock + 1; /* Highest fdset fd plus 1 */
116
+
117
+	for (;;) {
118
+		fd_set fds;
119
+		struct timeval now;
120
+		struct timeval wait;
121
+		int n;
122
+
123
+		/* Force timeout if we ran out of time */
124
+		gettimeofday(&now, 0);
125
+		if (timercmp(&now, &timeout, >)) {
126
+			logg("%s %s: connect timing out (%d secs)\n",
127
+			     __FILE__, __PRETTY_FUNCTION__,
128
+			     secs);
129
+			break; /* failed */
130
+		}
131
+
132
+		/* Calculate into 'wait' how long to wait */
133
+		timersub(&timeout, &now, &wait); /* wait = timeout - now */
134
+
135
+		/* Init fds with 'sock' as the only fd */
136
+		FD_ZERO(&fds);
137
+		FD_SET(sock, &fds);
138
+
139
+		n = select(numfd, 0, &fds, 0, &wait);
140
+		if (n < 0) {
141
+			logg("%s %s: select() failure %d: errno=%d: %s\n",
142
+			     __FILE__, __PRETTY_FUNCTION__,
143
+			     select_failures, errno, strerror(errno));
144
+			if (--select_failures >= 0)
145
+				continue; /* keep waiting */
146
+			break; /* failed */
147
+		}
148
+
149
+#ifdef NONBLOCK_DEBUG
150
+		logg("DEBUG %s %s: select = %d\n",
151
+		     __FILE__, __PRETTY_FUNCTION__,
152
+		     n);
153
+#endif
154
+
155
+		if (n) {
156
+			return connect_error(sock);
157
+		}
158
+
159
+		/* Select returned, but there is no work to do... */
160
+		if (--bogus_loops < 0) {
161
+			logg("%s %s: giving up due to excessive bogus loops\n",
162
+			     __FILE__, __PRETTY_FUNCTION__);
163
+			break; /* failed */
164
+		}
165
+
166
+	} /* for loop: keep waiting */
167
+
168
+	return -1; /* failed */
169
+}
170
+
171
+static ssize_t nonblock_recv(int sock, void *buf, size_t len, int flags, int secs)
172
+{
173
+	/* Max. of unexpected select() failures */
174
+	int select_failures = NONBLOCK_SELECT_MAX_FAILURES;
175
+	/* Max. of useless loops */
176
+	int bogus_loops = NONBLOCK_MAX_BOGUS_LOOPS;
177
+	struct timeval timeout;  /* When we should time out */
178
+	int numfd;               /* Highest fdset fd plus 1 */
179
+
180
+	/* Calculate into 'timeout' when we should time out */
181
+	gettimeofday(&timeout, 0);
182
+	timeout.tv_sec += secs;
183
+
184
+	numfd = sock + 1; /* Highest fdset fd plus 1 */
185
+
186
+	for (;;) {
187
+		fd_set fds;
188
+		struct timeval now;
189
+		struct timeval wait;
190
+		int n;
191
+
192
+		/* Force timeout if we ran out of time */
193
+		gettimeofday(&now, 0);
194
+		if (timercmp(&now, &timeout, >)) {
195
+			logg("%s %s: recv timing out (%d secs)\n",
196
+			     __FILE__, __PRETTY_FUNCTION__,
197
+			     secs);
198
+			break; /* failed */
199
+		}
200
+
201
+		/* Calculate into 'wait' how long to wait */
202
+		timersub(&timeout, &now, &wait); /* wait = timeout - now */
203
+
204
+		/* Init fds with 'sock' as the only fd */
205
+		FD_ZERO(&fds);
206
+		FD_SET(sock, &fds);
207
+
208
+		n = select(numfd, &fds, 0, 0, &wait);
209
+		if (n < 0) {
210
+			logg("%s %s: select() failure %d: errno=%d: %s\n",
211
+			     __FILE__, __PRETTY_FUNCTION__,
212
+			     select_failures, errno, strerror(errno));
213
+			if (--select_failures >= 0)
214
+				continue; /* keep waiting */
215
+			break; /* failed */
216
+		}
217
+
218
+		if (n) {
219
+			return recv(sock, buf, len, flags);
220
+		}
221
+
222
+		/* Select returned, but there is no work to do... */
223
+		if (--bogus_loops < 0) {
224
+			logg("%s %s: giving up due to excessive bogus loops\n",
225
+			     __FILE__, __PRETTY_FUNCTION__);
226
+			break; /* failed */
227
+		}
228
+
229
+	} /* for loop: keep waiting */
230
+
231
+	return -1; /* failed */
232
+}
233
+
234
+static long nonblock_fcntl(int sock)
235
+{
236
+	long fcntl_flags; /* Save fcntl() flags */
237
+
238
+	fcntl_flags = fcntl(sock, F_GETFL, 0);
239
+	if (fcntl_flags == -1) {
240
+		logg("%s %s: saving: fcntl(%d, F_GETFL): errno=%d: %s\n",
241
+		     __FILE__, __PRETTY_FUNCTION__,
242
+		     sock, errno, strerror(errno));
243
+	}
244
+	else if (fcntl(sock, F_SETFL, fcntl_flags | O_NONBLOCK)) {
245
+		logg("%s %s: fcntl(%d, F_SETFL, O_NONBLOCK): errno=%d: %s\n",
246
+		     __FILE__, __PRETTY_FUNCTION__,
247
+		     sock, errno, strerror(errno));
248
+	}
249
+
250
+	return fcntl_flags;
251
+}
252
+
253
+static void restore_fcntl(int sock, long fcntl_flags)
254
+{
255
+	if (fcntl_flags != -1) {
256
+		if (fcntl(sock, F_SETFL, fcntl_flags)) {
257
+			logg("%s %s: restoring: fcntl(%d, F_SETFL): errno=%d: %s\n",
258
+			     __FILE__, __PRETTY_FUNCTION__,
259
+			     sock, errno, strerror(errno));
260
+		}
261
+	}
262
+}
263
+
264
+/*
265
+	wait_connect(): wrapper for connect(), with explicit 'secs' timeout
266
+*/
267
+int wait_connect(int sock, const struct sockaddr *addr, socklen_t addrlen, int secs)
268
+{
269
+	long fcntl_flags; /* Save fcntl() flags */
270
+	int ret;
271
+
272
+	/* Temporarily set socket to non-blocking mode */
273
+	fcntl_flags = nonblock_fcntl(sock);
274
+
275
+	ret = nonblock_connect(sock, addr, addrlen, secs);
276
+
277
+	/* Restore socket's default blocking mode */
278
+	restore_fcntl(sock, fcntl_flags);
279
+
280
+	return ret;
281
+}
282
+
283
+/*
284
+	wait_recv(): wrapper for recv(), with explicit 'secs' timeout
285
+*/
286
+ssize_t wait_recv(int sock, void *buf, size_t len, int flags, int secs)
287
+{
288
+	long fcntl_flags; /* Save fcntl() flags */
289
+	int ret;
290
+
291
+	/* Temporarily set socket to non-blocking mode */
292
+	fcntl_flags = nonblock_fcntl(sock);
293
+
294
+	ret = nonblock_recv(sock, buf, len, flags, secs);
295
+
296
+	/* Restore socket's default blocking mode */
297
+	restore_fcntl(sock, fcntl_flags);
298
+
299
+	return ret;
300
+}
301
+
0 302
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+/*
1
+ *  Copyright 2006 Everton da Silva Marques <everton.marques@gmail.com>
2
+ *
3
+ *  This program is free software; you can redistribute it and/or modify
4
+ *  it under the terms of the GNU General Public License as published by
5
+ *  the Free Software Foundation; either version 2 of the License, or
6
+ *  (at your option) any later version.
7
+ *
8
+ *  This program is distributed in the hope that it will be useful,
9
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+ *  GNU General Public License for more details.
12
+ *
13
+ *  You should have received a copy of the GNU General Public License
14
+ *  along with this program; if not, write to the Free Software
15
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
+ */
17
+
18
+#ifndef __NONBLOCK_H
19
+#define __NONBLOCK_H
20
+
21
+/*
22
+	wait_connect(): wrapper for connect(), with explicit 'secs' timeout
23
+*/
24
+int wait_connect(int sock, const struct sockaddr *addr, socklen_t addrlen, int secs);
25
+
26
+/*
27
+        wait_recv(): wrapper for recv(), with explicit 'secs' timeout
28
+*/
29
+ssize_t wait_recv(int sock, void *buf, size_t len, int flags, int secs);
30
+
31
+#endif /* NONBLOCK_H */
... ...
@@ -106,6 +106,8 @@ struct cfgoption cfg_options[] = {
106 106
     {"OnErrorExecute", OPT_FULLSTR, -1, NULL, 0, OPT_FRESHCLAM},
107 107
     {"OnOutdatedExecute", OPT_FULLSTR, -1, NULL, 0, OPT_FRESHCLAM},
108 108
     {"LocalIPAddress", OPT_STR, -1, NULL, 0, OPT_FRESHCLAM},
109
+    {"ConnectTimeout", OPT_NUM, 10, NULL, 0, OPT_FRESHCLAM},
110
+    {"ReceiveTimeout", OPT_NUM, 30, NULL, 0, OPT_FRESHCLAM},
109 111
     {NULL, 0, 0, NULL, 0, 0}
110 112
 };
111 113