git-svn: trunk@4798
Török Edvin authored on 2009/02/17 03:27:08... | ... |
@@ -1,3 +1,12 @@ |
1 |
+Mon Feb 16 20:36:01 EET 2009 (edwin) |
|
2 |
+------------------------------------ |
|
3 |
+ * clamd/, libclamav/, shared/, unit_tests/: introduce timeouts for |
|
4 |
+ send(), this is needed for IDSESSION to work reliably, since a buggy |
|
5 |
+ client implementation may get stuck in send(), and then clamd gets |
|
6 |
+ stuck in send() -> deadlock. To avoid this we need nonblocking |
|
7 |
+ sockets, and (low) timeouts on send. Add more tests for clamd protocol, |
|
8 |
+ including a stress test for IDSESSION. |
|
9 |
+ |
|
1 | 10 |
Mon Feb 16 16:15:21 EET 2009 (edwin) |
2 | 11 |
------------------------------------ |
3 | 12 |
* unit_tests/check_clamd.c: test for FILDES |
... | ... |
@@ -190,7 +190,8 @@ int scan_callback(struct stat *sb, char *filename, const char *msg, enum cli_ftw |
190 | 190 |
} |
191 | 191 |
|
192 | 192 |
if (access(filename, R_OK)) { |
193 |
- conn_reply(scandata->conn, filename, "Access denied.", "ERROR"); |
|
193 |
+ if (conn_reply(scandata->conn, filename, "Access denied.", "ERROR") == -1) |
|
194 |
+ return CL_ETIMEOUT; |
|
194 | 195 |
logg("*Access denied: %s\n", filename); |
195 | 196 |
scandata->errors++; |
196 | 197 |
free(filename); |
... | ... |
@@ -202,14 +203,21 @@ int scan_callback(struct stat *sb, char *filename, const char *msg, enum cli_ftw |
202 | 202 |
ret = cl_scanfile(filename, &virname, &scandata->scanned, scandata->engine, scandata->options); |
203 | 203 |
thrmgr_setactivetask(NULL, NULL); |
204 | 204 |
|
205 |
+ if (thrmgr_group_need_terminate(scandata->conn->group)) { |
|
206 |
+ logg("*Client disconnected while scanjob was active\n"); |
|
207 |
+ return ret == CL_ETIMEOUT ? ret : CL_BREAK; |
|
208 |
+ } |
|
209 |
+ |
|
205 | 210 |
if (ret == CL_VIRUS) { |
206 | 211 |
scandata->infected++; |
207 |
- conn_reply(scandata->conn, filename, virname, "FOUND"); |
|
212 |
+ if (conn_reply(scandata->conn, filename, virname, "FOUND") == -1) |
|
213 |
+ return CL_ETIMEOUT; |
|
208 | 214 |
logg("~%s: %s FOUND\n", filename, virname); |
209 | 215 |
virusaction(filename, virname, scandata->opts); |
210 | 216 |
} else if (ret != CL_CLEAN) { |
211 | 217 |
scandata->errors++; |
212 |
- conn_reply(scandata->conn, filename, cl_strerror(ret), "ERROR"); |
|
218 |
+ if (conn_reply(scandata->conn, filename, cl_strerror(ret), "ERROR") == -1) |
|
219 |
+ return CL_ETIMEOUT; |
|
213 | 220 |
logg("~%s: %s ERROR\n", filename, cl_strerror(ret)); |
214 | 221 |
} else if (logok) { |
215 | 222 |
logg("~%s: OK\n", filename); |
... | ... |
@@ -219,10 +227,6 @@ int scan_callback(struct stat *sb, char *filename, const char *msg, enum cli_ftw |
219 | 219 |
if(ret == CL_EMEM) /* stop scanning */ |
220 | 220 |
return ret; |
221 | 221 |
|
222 |
- if (thrmgr_group_need_terminate(scandata->conn->group)) { |
|
223 |
- logg("^Client disconnected while scanjob was active\n"); |
|
224 |
- return CL_BREAK; |
|
225 |
- } |
|
226 | 222 |
if (type == TYPE_SCAN) { |
227 | 223 |
/* virus -> break */ |
228 | 224 |
return ret; |
... | ... |
@@ -246,8 +250,9 @@ int scanfd(const int fd, const client_conn_t *conn, unsigned long int *scanned, |
246 | 246 |
else |
247 | 247 |
snprintf(fdstr, sizeof(fdstr), "fd[%d]", fd); |
248 | 248 |
if(fstat(fd, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) { |
249 |
- conn_reply(conn, fdstr, "Not a regular file", "ERROR"); |
|
250 | 249 |
logg("%s: Not a regular file. ERROR\n", fdstr); |
250 |
+ if (conn_reply(conn, fdstr, "Not a regular file", "ERROR") == -1) |
|
251 |
+ return CL_ETIMEOUT; |
|
251 | 252 |
return -1; |
252 | 253 |
} |
253 | 254 |
|
... | ... |
@@ -255,15 +260,23 @@ int scanfd(const int fd, const client_conn_t *conn, unsigned long int *scanned, |
255 | 255 |
ret = cl_scandesc(fd, &virname, scanned, engine, options); |
256 | 256 |
thrmgr_setactivetask(NULL, NULL); |
257 | 257 |
|
258 |
+ if (thrmgr_group_need_terminate(conn->group)) { |
|
259 |
+ logg("*Client disconnected while scanjob was active\n"); |
|
260 |
+ return ret == CL_ETIMEOUT ? ret : CL_BREAK; |
|
261 |
+ } |
|
262 |
+ |
|
258 | 263 |
if(ret == CL_VIRUS) { |
259 |
- conn_reply(conn, fdstr, virname, "FOUND"); |
|
264 |
+ if (conn_reply(conn, fdstr, virname, "FOUND") == -1) |
|
265 |
+ ret = CL_ETIMEOUT; |
|
260 | 266 |
logg("%s: %s FOUND\n", fdstr, virname); |
261 | 267 |
virusaction(fdstr, virname, opts); |
262 | 268 |
} else if(ret != CL_CLEAN) { |
263 |
- conn_reply(conn, fdstr, cl_strerror(ret), "ERROR"); |
|
269 |
+ if (conn_reply(conn, fdstr, cl_strerror(ret), "ERROR") == -1) |
|
270 |
+ ret = CL_ETIMEOUT; |
|
264 | 271 |
logg("%s: %s ERROR\n", fdstr, cl_strerror(ret)); |
265 | 272 |
} else { |
266 |
- conn_reply_single(conn, fdstr, "OK"); |
|
273 |
+ if (conn_reply_single(conn, fdstr, "OK") == CL_ETIMEOUT) |
|
274 |
+ ret = CL_ETIMEOUT; |
|
267 | 275 |
if(logok) |
268 | 276 |
logg("%s: OK\n", fdstr); |
269 | 277 |
} |
... | ... |
@@ -43,6 +43,7 @@ |
43 | 43 |
#include <unistd.h> |
44 | 44 |
#endif |
45 | 45 |
|
46 |
+#include <fcntl.h> |
|
46 | 47 |
#include <arpa/inet.h> |
47 | 48 |
#include "libclamav/clamav.h" |
48 | 49 |
|
... | ... |
@@ -358,7 +359,20 @@ static void *acceptloop_th(void *arg) |
358 | 358 |
pthread_mutex_unlock(&exit_mutex); |
359 | 359 |
|
360 | 360 |
if (new_sd >= 0) { |
361 |
- int ret; |
|
361 |
+ int ret, flags; |
|
362 |
+ |
|
363 |
+#ifdef F_GETFL |
|
364 |
+ flags = fcntl(new_sd, F_GETFL, 0); |
|
365 |
+ if (flags != -1) { |
|
366 |
+ if (fcntl(new_sd, F_SETFL, flags | O_NONBLOCK) == -1) { |
|
367 |
+ logg("^Can't set socket to nonblocking mode, errno %d\n", |
|
368 |
+ errno); |
|
369 |
+ } |
|
370 |
+ } else { |
|
371 |
+ logg("^Can't get socket flags, errno %d\n", errno); |
|
372 |
+ } |
|
373 |
+#endif |
|
374 |
+ |
|
362 | 375 |
pthread_mutex_lock(&recv_fds->buf_mutex); |
363 | 376 |
ret = fds_add(recv_fds, new_sd, 0); |
364 | 377 |
pthread_mutex_unlock(&recv_fds->buf_mutex); |
... | ... |
@@ -882,6 +896,11 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
882 | 882 |
} |
883 | 883 |
error = 1; |
884 | 884 |
} |
885 |
+ if (thrmgr_group_need_terminate(conn.group)) { |
|
886 |
+ logg("*RECVTH: have to terminate group\n"); |
|
887 |
+ error = CL_ETIMEOUT; |
|
888 |
+ break; |
|
889 |
+ } |
|
885 | 890 |
if (error || !conn.group || rc) { |
886 | 891 |
if (rc && thrmgr_group_finished(conn.group, EXIT_OK)) { |
887 | 892 |
logg("*RECVTH: closing conn, group finished\n"); |
... | ... |
@@ -1023,7 +1042,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
1023 | 1023 |
buf->off = 0; |
1024 | 1024 |
} |
1025 | 1025 |
} |
1026 |
- if (error) { |
|
1026 |
+ if (error && error != CL_ETIMEOUT) { |
|
1027 | 1027 |
conn_reply_error(&conn, "Error processing command."); |
1028 | 1028 |
} |
1029 | 1029 |
} |
... | ... |
@@ -1036,7 +1055,8 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
1036 | 1036 |
} |
1037 | 1037 |
buf->dumpfd = -1; |
1038 | 1038 |
} |
1039 |
- if (thrmgr_group_terminate(buf->group)) { |
|
1039 |
+ thrmgr_group_terminate(buf->group); |
|
1040 |
+ if (thrmgr_group_finished(buf->group, EXIT_ERROR)) { |
|
1040 | 1041 |
logg("*RECVTH: shutting down socket after error\n"); |
1041 | 1042 |
shutdown(buf->fd, 2); |
1042 | 1043 |
closesocket(buf->fd); |
... | ... |
@@ -1055,7 +1075,8 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
1055 | 1055 |
for (i=0;i < fds->nfds; i++) { |
1056 | 1056 |
if (fds->buf[i].fd == -1) |
1057 | 1057 |
continue; |
1058 |
- if (thrmgr_group_terminate(fds->buf[i].group)) { |
|
1058 |
+ thrmgr_group_terminate(fds->buf[i].group); |
|
1059 |
+ if (thrmgr_group_finished(fds->buf[i].group, EXIT_ERROR)) { |
|
1059 | 1060 |
shutdown(fds->buf[i].fd, 2); |
1060 | 1061 |
closesocket(fds->buf[i].fd); |
1061 | 1062 |
fds->buf[i].fd = -1; |
... | ... |
@@ -183,7 +183,7 @@ int command(client_conn_t *conn, int *virus) |
183 | 183 |
jobgroup_t *group = NULL; |
184 | 184 |
|
185 | 185 |
if (thrmgr_group_need_terminate(conn->group)) { |
186 |
- logg("^Client disconnected while command was active\n"); |
|
186 |
+ logg("*Client disconnected while command was active\n"); |
|
187 | 187 |
if (conn->scanfd != -1) |
188 | 188 |
close(conn->scanfd); |
189 | 189 |
return 1; |
... | ... |
@@ -235,11 +235,14 @@ int command(client_conn_t *conn, int *virus) |
235 | 235 |
conn_reply_error(conn, "FILDES: didn't receive file descriptor."); |
236 | 236 |
else { |
237 | 237 |
ret = scanfd(conn->scanfd, conn, NULL, engine, options, opts, desc, 0); |
238 |
- if (ret == CL_VIRUS) |
|
238 |
+ if (ret == CL_VIRUS) { |
|
239 | 239 |
*virus = 1; |
240 |
- if (ret == CL_EMEM) { |
|
240 |
+ } else if (ret == CL_EMEM) { |
|
241 | 241 |
if(optget(opts, "ExitOnOOM")->enabled) |
242 | 242 |
ret = -1; |
243 |
+ } else if (ret == CL_ETIMEOUT) { |
|
244 |
+ thrmgr_group_terminate(conn->group); |
|
245 |
+ ret = 1; |
|
243 | 246 |
} else |
244 | 247 |
ret = 0; |
245 | 248 |
} |
... | ... |
@@ -269,11 +272,14 @@ int command(client_conn_t *conn, int *virus) |
269 | 269 |
return 0; |
270 | 270 |
case COMMAND_INSTREAMSCAN: |
271 | 271 |
ret = scanfd(conn->scanfd, conn, NULL, engine, options, opts, desc, 1); |
272 |
- if (ret == CL_VIRUS) |
|
272 |
+ if (ret == CL_VIRUS) { |
|
273 | 273 |
*virus = 1; |
274 |
- if (ret == CL_EMEM) { |
|
274 |
+ } else if (ret == CL_EMEM) { |
|
275 | 275 |
if(optget(opts, "ExitOnOOM")->enabled) |
276 | 276 |
ret = -1; |
277 |
+ } else if (ret == CL_ETIMEOUT) { |
|
278 |
+ thrmgr_group_terminate(conn->group); |
|
279 |
+ ret = 1; |
|
277 | 280 |
} else |
278 | 281 |
ret = 0; |
279 | 282 |
if (ftruncate(conn->scanfd, 0) == -1) { |
... | ... |
@@ -292,7 +298,8 @@ int command(client_conn_t *conn, int *virus) |
292 | 292 |
flags |= CLI_FTW_FOLLOW_DIR_SYMLINK; |
293 | 293 |
if (optget(opts, "FollowFileSymlinks")->enabled) |
294 | 294 |
flags |= CLI_FTW_FOLLOW_FILE_SYMLINK; |
295 |
- if (cli_ftw(conn->filename, flags, maxdirrec ? maxdirrec : INT_MAX, scan_callback, &data) == CL_EMEM) |
|
295 |
+ ret = cli_ftw(conn->filename, flags, maxdirrec ? maxdirrec : INT_MAX, scan_callback, &data); |
|
296 |
+ if (ret == CL_EMEM) |
|
296 | 297 |
if(optget(opts, "ExitOnOOM")->enabled) |
297 | 298 |
return -1; |
298 | 299 |
if (scandata.group && conn->cmdtype == COMMAND_MULTISCAN) { |
... | ... |
@@ -304,9 +311,13 @@ int command(client_conn_t *conn, int *virus) |
304 | 304 |
} |
305 | 305 |
|
306 | 306 |
if (ok + error == total && (error != total)) { |
307 |
- conn_reply_single(conn, conn->filename, "OK"); |
|
307 |
+ if (conn_reply_single(conn, conn->filename, "OK") == -1) |
|
308 |
+ ret = CL_ETIMEOUT; |
|
308 | 309 |
} |
309 | 310 |
*virus = total - (ok + error); |
311 |
+ |
|
312 |
+ if (ret == CL_ETIMEOUT) |
|
313 |
+ thrmgr_group_terminate(conn->group); |
|
310 | 314 |
return error; |
311 | 315 |
} |
312 | 316 |
|
... | ... |
@@ -828,18 +828,13 @@ int thrmgr_group_need_terminate(jobgroup_t *group) |
828 | 828 |
return ret; |
829 | 829 |
} |
830 | 830 |
|
831 |
-/* returns |
|
832 |
- * 0 - flag set, jobs still active |
|
833 |
- * 1 - last job exited */ |
|
834 |
-int thrmgr_group_terminate(jobgroup_t *group) |
|
831 |
+void thrmgr_group_terminate(jobgroup_t *group) |
|
835 | 832 |
{ |
836 |
- if (thrmgr_group_finished(group, EXIT_ERROR)) |
|
837 |
- return 1; |
|
838 |
- /* we are not last active job, now |
|
839 |
- * the last active job will free resources */ |
|
840 |
- pthread_mutex_lock(&group->mutex); |
|
841 |
- group->force_exit = 1; |
|
842 |
- pthread_mutex_unlock(&group->mutex); |
|
843 |
- |
|
844 |
- return 0; |
|
833 |
+ if (group) { |
|
834 |
+ /* we may not be the last active job, now |
|
835 |
+ * the last active job will free resources */ |
|
836 |
+ pthread_mutex_lock(&group->mutex); |
|
837 |
+ group->force_exit = 1; |
|
838 |
+ pthread_mutex_unlock(&group->mutex); |
|
839 |
+ } |
|
845 | 840 |
} |
... | ... |
@@ -101,7 +101,7 @@ int thrmgr_group_dispatch(threadpool_t *threadpool, jobgroup_t *group, void *use |
101 | 101 |
void thrmgr_group_waitforall(jobgroup_t *group, unsigned *ok, unsigned *error, unsigned *total); |
102 | 102 |
int thrmgr_group_finished(jobgroup_t *group, enum thrmgr_exit exitc); |
103 | 103 |
int thrmgr_group_need_terminate(jobgroup_t *group); |
104 |
-int thrmgr_group_terminate(jobgroup_t *group); |
|
104 |
+void thrmgr_group_terminate(jobgroup_t *group); |
|
105 | 105 |
jobgroup_t *thrmgr_group_new(void); |
106 | 106 |
int thrmgr_printstats(int outfd); |
107 | 107 |
void thrmgr_setactivetask(const char *filename, const char* command); |
... | ... |
@@ -47,6 +47,10 @@ |
47 | 47 |
#include <sys/types.h> |
48 | 48 |
#endif |
49 | 49 |
|
50 |
+#ifdef HAVE_SYS_SELECT_H |
|
51 |
+#include <sys/select.h> |
|
52 |
+#endif |
|
53 |
+ |
|
50 | 54 |
#if defined(USE_SYSLOG) && !defined(C_AIX) |
51 | 55 |
#include <syslog.h> |
52 | 56 |
#endif |
... | ... |
@@ -88,7 +92,7 @@ short logg_syslog; |
88 | 88 |
#endif |
89 | 89 |
|
90 | 90 |
short int mprintf_disabled = 0, mprintf_verbose = 0, mprintf_quiet = 0, |
91 |
- mprintf_stdout = 0, mprintf_nowarn = 0; |
|
91 |
+ mprintf_stdout = 0, mprintf_nowarn = 0, mprintf_send_timeout = 100; |
|
92 | 92 |
|
93 | 93 |
#define ARGLEN(args, str, len) \ |
94 | 94 |
{ \ |
... | ... |
@@ -127,7 +131,7 @@ int mdprintf(int desc, const char *str, ...) |
127 | 127 |
{ |
128 | 128 |
va_list args; |
129 | 129 |
char buffer[512], *abuffer = NULL, *buff; |
130 |
- int bytes, len, ret; |
|
130 |
+ int bytes, todo, len, ret; |
|
131 | 131 |
|
132 | 132 |
|
133 | 133 |
ARGLEN(args, str, len); |
... | ... |
@@ -156,12 +160,36 @@ int mdprintf(int desc, const char *str, ...) |
156 | 156 |
if((size_t) bytes >= len) |
157 | 157 |
bytes = len - 1; |
158 | 158 |
|
159 |
- ret = send(desc, buff, bytes, 0); |
|
159 |
+ todo = bytes; |
|
160 |
+ while (todo > 0) { |
|
161 |
+ ret = send(desc, buff, bytes, 0); |
|
162 |
+ if (ret < 0) { |
|
163 |
+ struct timeval tv; |
|
164 |
+ if (errno != EWOULDBLOCK) |
|
165 |
+ break; |
|
166 |
+ tv.tv_sec = 0; |
|
167 |
+ tv.tv_usec = mprintf_send_timeout*1000; |
|
168 |
+ do { |
|
169 |
+ fd_set wfds; |
|
170 |
+ FD_ZERO(&wfds); |
|
171 |
+ FD_SET(desc, &wfds); |
|
172 |
+ ret = select(desc+1, NULL, &wfds, NULL, &tv); |
|
173 |
+ } while (ret < 0 && errno == EINTR); |
|
174 |
+ if (!ret) { |
|
175 |
+ /* timed out */ |
|
176 |
+ ret = -1; |
|
177 |
+ break; |
|
178 |
+ } |
|
179 |
+ } else { |
|
180 |
+ todo -= ret; |
|
181 |
+ buff += ret; |
|
182 |
+ } |
|
183 |
+ } |
|
160 | 184 |
|
161 | 185 |
if(len > sizeof(buffer)) |
162 | 186 |
free(abuffer); |
163 | 187 |
|
164 |
- return bytes; |
|
188 |
+ return ret < 0 ? -1 : bytes; |
|
165 | 189 |
} |
166 | 190 |
|
167 | 191 |
void logg_close(void) |
... | ... |
@@ -162,7 +162,7 @@ static void test_command(const char *cmd, size_t len, const char *extra, const c |
162 | 162 |
|
163 | 163 |
if (extra) { |
164 | 164 |
rc = send(sockd, extra, strlen(extra), 0); |
165 |
- fail_unless_fmt((size_t)rc == strlen(extra), "Unable to send(): %s\n", strerror(errno)); |
|
165 |
+ fail_unless_fmt((size_t)rc == strlen(extra), "Unable to send() extra for %s: %s\n", cmd, strerror(errno)); |
|
166 | 166 |
} |
167 | 167 |
|
168 | 168 |
recvdata = recvfull(sockd, &len); |
... | ... |
@@ -215,11 +215,15 @@ START_TEST (test_compat_commands) |
215 | 215 |
test_command(nsend, strlen(nsend), test->extra, nreply, strlen(nreply)); |
216 | 216 |
conn_teardown(); |
217 | 217 |
|
218 |
- /* one packet, \r\n delimited command, followed by "extra" if needed */ |
|
219 |
- snprintf(nsend, sizeof(nsend), "%s\r\n", test->command); |
|
220 |
- conn_setup(); |
|
221 |
- test_command(nsend, strlen(nsend), test->extra, nreply, strlen(nreply)); |
|
222 |
- conn_teardown(); |
|
218 |
+ if (!test->extra) { |
|
219 |
+ /* FILDES won't support this, because it expects |
|
220 |
+ * strlen("FILDES\n") characters, then 1 character and the FD. */ |
|
221 |
+ /* one packet, \r\n delimited command, followed by "extra" if needed */ |
|
222 |
+ snprintf(nsend, sizeof(nsend), "%s\r\n", test->command); |
|
223 |
+ conn_setup(); |
|
224 |
+ test_command(nsend, strlen(nsend), test->extra, nreply, strlen(nreply)); |
|
225 |
+ conn_teardown(); |
|
226 |
+ } |
|
223 | 227 |
} |
224 | 228 |
END_TEST |
225 | 229 |
|
... | ... |
@@ -295,22 +299,28 @@ START_TEST (test_instream) |
295 | 295 |
} |
296 | 296 |
END_TEST |
297 | 297 |
|
298 |
-static void tst_fildes(const char *cmd, size_t len, int fd, |
|
299 |
- const char *expect, size_t expect_len, int closefd) |
|
298 |
+static int sendmsg_fd(int sockd, const char *mesg, size_t msg_len, int fd, int singlemsg) |
|
300 | 299 |
{ |
301 | 300 |
struct msghdr msg; |
302 | 301 |
struct cmsghdr *cmsg; |
303 | 302 |
unsigned char fdbuf[CMSG_SPACE(sizeof(int))]; |
304 |
- char dummy[] = ""; |
|
305 |
- off_t pos; |
|
303 |
+ char dummy[BUFSIZ]; |
|
306 | 304 |
struct iovec iov[1]; |
307 |
- char *recvdata, *p; |
|
308 | 305 |
int rc; |
309 | 306 |
|
310 |
- conn_setup(); |
|
311 |
- |
|
312 |
- iov[0].iov_base = dummy; |
|
313 |
- iov[0].iov_len = 1; |
|
307 |
+ if (!singlemsg) { |
|
308 |
+ /* send FILDES\n and then a single character + ancillary data */ |
|
309 |
+ dummy[0] = '\0'; |
|
310 |
+ iov[0].iov_base = dummy; |
|
311 |
+ iov[0].iov_len = 1; |
|
312 |
+ } else { |
|
313 |
+ /* send single message with ancillary data */ |
|
314 |
+ fail_unless(msg_len < sizeof(dummy)-1); |
|
315 |
+ memcpy(dummy, mesg, msg_len); |
|
316 |
+ dummy[msg_len] = '\0'; |
|
317 |
+ iov[0].iov_base = dummy; |
|
318 |
+ iov[0].iov_len = msg_len + 1; |
|
319 |
+ } |
|
314 | 320 |
|
315 | 321 |
memset(&msg, 0, sizeof(msg)); |
316 | 322 |
msg.msg_control = fdbuf; |
... | ... |
@@ -324,10 +334,25 @@ static void tst_fildes(const char *cmd, size_t len, int fd, |
324 | 324 |
cmsg->cmsg_type = SCM_RIGHTS; |
325 | 325 |
*(int *)CMSG_DATA(cmsg) = fd; |
326 | 326 |
|
327 |
- fail_unless_fmt(write(sockd, cmd, len) == len, "Failed to write: %s\n", strerror(errno)); |
|
327 |
+ if (!singlemsg) { |
|
328 |
+ rc = send(sockd, mesg, msg_len, 0); |
|
329 |
+ if (rc == -1) |
|
330 |
+ return rc; |
|
331 |
+ } |
|
332 |
+ |
|
333 |
+ return sendmsg(sockd, &msg, 0); |
|
334 |
+} |
|
335 |
+ |
|
336 |
+static void tst_fildes(const char *cmd, size_t len, int fd, |
|
337 |
+ const char *expect, size_t expect_len, int closefd, int singlemsg) |
|
338 |
+{ |
|
339 |
+ off_t pos; |
|
340 |
+ char *recvdata, *p; |
|
341 |
+ int rc; |
|
328 | 342 |
|
329 |
- rc = sendmsg(sockd, &msg, 0); |
|
330 |
- fail_unless_fmt(rc != -1, "Failed to sendmsg: %s\n", strerror(errno)); |
|
343 |
+ conn_setup(); |
|
344 |
+ fail_unless_fmt(sendmsg_fd(sockd, cmd, len, fd, singlemsg) != -1, |
|
345 |
+ "Failed to sendmsg: %s\n", strerror(errno)); |
|
331 | 346 |
|
332 | 347 |
if (closefd) |
333 | 348 |
close(fd); |
... | ... |
@@ -353,48 +378,110 @@ static void tst_fildes(const char *cmd, size_t len, int fd, |
353 | 353 |
#define FOUNDFDREPLY " ClamAV-Test-File.UNOFFICIAL FOUND" |
354 | 354 |
#define CLEANFDREPLY " OK" |
355 | 355 |
|
356 |
+static struct cmds { |
|
357 |
+ const char *cmd; |
|
358 |
+ const char term; |
|
359 |
+ const char *file; |
|
360 |
+ const char *reply; |
|
361 |
+} fildes_cmds[] = |
|
362 |
+{ |
|
363 |
+ {"FILDES", '\n', SCANFILE, FOUNDFDREPLY}, |
|
364 |
+ {"nFILDES", '\n', SCANFILE, FOUNDFDREPLY}, |
|
365 |
+ {"zFILDES", '\0', SCANFILE, FOUNDFDREPLY}, |
|
366 |
+ {"FILDES", '\n', CLEANFILE, CLEANFDREPLY}, |
|
367 |
+ {"nFILDES", '\n', CLEANFILE, CLEANFDREPLY}, |
|
368 |
+ {"zFILDES", '\0', CLEANFILE, CLEANFDREPLY} |
|
369 |
+}; |
|
370 |
+ |
|
356 | 371 |
START_TEST (test_fildes) |
357 | 372 |
{ |
358 |
- char nreply[BUFSIZ]; |
|
373 |
+ char nreply[BUFSIZ], nsend[BUFSIZ]; |
|
359 | 374 |
int fd = open(SCANFILE, O_RDONLY); |
375 |
+ int closefd; |
|
376 |
+ int singlemsg; |
|
377 |
+ size_t i; |
|
378 |
+ const struct cmds *cmd; |
|
379 |
+ size_t nreply_len, nsend_len; |
|
380 |
+ |
|
381 |
+ switch (_i&3) { |
|
382 |
+ case 0: |
|
383 |
+ closefd = 0; |
|
384 |
+ singlemsg = 0; |
|
385 |
+ break; |
|
386 |
+ case 1: |
|
387 |
+ closefd = 1; |
|
388 |
+ singlemsg = 0; |
|
389 |
+ break; |
|
390 |
+ case 2: |
|
391 |
+ closefd = 0; |
|
392 |
+ singlemsg = 1; |
|
393 |
+ break; |
|
394 |
+ case 3: |
|
395 |
+ closefd = 1; |
|
396 |
+ singlemsg = 1; |
|
397 |
+ break; |
|
398 |
+ } |
|
360 | 399 |
|
361 |
- snprintf(nreply, sizeof(nreply), "%s\n", FOUNDFDREPLY); |
|
400 |
+ cmd = &fildes_cmds[_i/4]; |
|
401 |
+ nreply_len = snprintf(nreply, sizeof(nreply), "%s%c", cmd->reply, cmd->term); |
|
402 |
+ nsend_len = snprintf(nsend, sizeof(nsend), "%s%c", cmd->cmd, cmd->term); |
|
362 | 403 |
|
404 |
+ fd = open(cmd->file, O_RDONLY); |
|
363 | 405 |
fail_unless_fmt(fd != -1, "Failed to open: %s\n", strerror(errno)); |
364 | 406 |
|
365 |
- tst_fildes("FILDES\n", strlen("FILDES\n"), fd, nreply, strlen(nreply), 0); |
|
366 |
- tst_fildes("nFILDES\n", strlen("nFILDES\n"), fd, nreply, strlen(nreply), 0); |
|
367 |
- tst_fildes("zFILDES", sizeof("zFILDES"), fd, FOUNDFDREPLY, sizeof(FOUNDFDREPLY), 0); |
|
407 |
+ tst_fildes(nsend, nsend_len, fd, nreply, nreply_len, closefd, singlemsg); |
|
368 | 408 |
|
369 |
- close(fd); |
|
409 |
+ if (!closefd) { |
|
410 |
+ /* closefd: |
|
411 |
+ * 1 - close fd right after sending |
|
412 |
+ * 0 - close fd after receiving reply */ |
|
413 |
+ close(fd); |
|
414 |
+ } |
|
415 |
+} |
|
416 |
+END_TEST |
|
370 | 417 |
|
371 |
- snprintf(nreply, sizeof(nreply), "%s\n", CLEANFDREPLY); |
|
418 |
+START_TEST (test_fildes_many) |
|
419 |
+{ |
|
420 |
+ const char idsession[] = "zIDSESSION"; |
|
421 |
+ int dummyfd, dummycleanfd, i, killed = 0; |
|
372 | 422 |
|
373 |
- fd = open(CLEANFILE, O_RDONLY); |
|
374 |
- fail_unless_fmt(fd != -1, "Failed to open: %s\n", strerror(errno)); |
|
423 |
+ conn_setup(); |
|
424 |
+ dummyfd = open(SCANFILE, O_RDONLY); |
|
425 |
+ fail_unless_fmt(dummyfd != -1, "failed to open %s: %s\n", SCANFILE, strerror(errno)); |
|
426 |
+ |
|
427 |
+ fail_unless_fmt(send(sockd, idsession, sizeof(idsession), 0) == sizeof(idsession), "send IDSESSION failed\n"); |
|
428 |
+ for (i=0; i < 2048; i++) { |
|
429 |
+ if (sendmsg_fd(sockd, "zFILDES", sizeof("zFILDES"), dummyfd, 1) == -1) { |
|
430 |
+ killed = 1; |
|
431 |
+ break; |
|
432 |
+ } |
|
433 |
+ } |
|
375 | 434 |
|
376 |
- tst_fildes("FILDES\n", strlen("FILDES\n"), fd, nreply, strlen(nreply), 0); |
|
377 |
- tst_fildes("nFILDES\n", strlen("nFILDES\n"), fd, nreply, strlen(nreply), 0); |
|
378 |
- tst_fildes("zFILDES", sizeof("zFILDES"), fd, CLEANFDREPLY, sizeof(CLEANFDREPLY), 0); |
|
435 |
+ fail_unless(killed, "Clamd did not kill connection when overloaded!\n"); |
|
379 | 436 |
|
380 |
- snprintf(nreply, sizeof(nreply), "%s\n", FOUNDFDREPLY); |
|
381 |
- fd = open(SCANFILE, O_RDONLY); |
|
382 |
- fail_unless_fmt(fd != -1, "Failed to open: %s\n", strerror(errno)); |
|
383 |
- tst_fildes("FILDES\n", strlen("FILDES\n"), fd, nreply, strlen(nreply), 1); |
|
437 |
+ close(dummyfd); |
|
438 |
+ conn_teardown(); |
|
384 | 439 |
} |
385 | 440 |
END_TEST |
386 | 441 |
|
387 | 442 |
static Suite *test_clamd_suite(void) |
388 | 443 |
{ |
389 | 444 |
Suite *s = suite_create("clamd"); |
390 |
- TCase *tc_commands = tcase_create("clamd commands"); |
|
445 |
+ TCase *tc_commands, *tc_stress; |
|
446 |
+ |
|
447 |
+ tc_commands = tcase_create("clamd commands"); |
|
391 | 448 |
suite_add_tcase(s, tc_commands); |
392 | 449 |
tcase_add_unchecked_fixture(tc_commands, commands_setup, commands_teardown); |
393 | 450 |
tcase_add_loop_test(tc_commands, test_basic_commands, 0, sizeof(basic_tests)/sizeof(basic_tests[0])); |
394 | 451 |
tcase_add_loop_test(tc_commands, test_compat_commands, 0, sizeof(basic_tests)/sizeof(basic_tests[0])); |
395 |
- tcase_add_test(tc_commands, test_fildes); |
|
452 |
+ tcase_add_loop_test(tc_commands, test_fildes, 0, 4*sizeof(fildes_cmds)/sizeof(fildes_cmds[0])); |
|
396 | 453 |
tcase_add_test(tc_commands, test_stats); |
397 | 454 |
tcase_add_test(tc_commands, test_instream); |
455 |
+ |
|
456 |
+ tc_stress = tcase_create("clamd stress test"); |
|
457 |
+ suite_add_tcase(s, tc_stress); |
|
458 |
+ tcase_add_test(tc_stress, test_fildes_many); |
|
459 |
+ |
|
398 | 460 |
return s; |
399 | 461 |
} |
400 | 462 |
|