git-svn: trunk@2233
Tomasz Kojm authored on 2006/09/05 07:30:09... | ... |
@@ -1,3 +1,11 @@ |
1 |
+Tue Sep 5 00:23:26 CEST 2006 (tk) |
|
2 |
+---------------------------------- |
|
3 |
+ * clamd: all commands can be now prefixed with the letter 'n' (eg. nSCAN) to |
|
4 |
+ to indicate that they will be delimited by a new line character |
|
5 |
+ (which assures that the complete command and its entire argument |
|
6 |
+ will be processed as a single command) |
|
7 |
+ Patch from Mark Pizzolato <clamav-devel*subscriptions.pizzolato.net> |
|
8 |
+ |
|
1 | 9 |
Mon Sep 4 21:06:52 CEST 2006 (tk) |
2 | 10 |
---------------------------------- |
3 | 11 |
* libclamav/unrar/unrarvm.c: fix possible crash reported by Sven |
... | ... |
@@ -303,14 +303,27 @@ int writen(int fd, void *buff, unsigned int count) |
303 | 303 |
return count; |
304 | 304 |
} |
305 | 305 |
|
306 |
-/* Submitted by Richard Lyons <frob-clamav*webcentral.com.au> */ |
|
307 |
- |
|
308 |
-#if defined(HAVE_RECVMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR)) && !defined(C_CYGWIN) && !defined(C_OS2) && !defined(INCOMPLETE_CMSG) |
|
309 |
- |
|
310 |
-int readsock(int sockfd, char *buf, size_t size) |
|
306 |
+/* FD Support Submitted by Richard Lyons <frob-clamav*webcentral.com.au> */ |
|
307 |
+/* |
|
308 |
+ This procedure does timed clamd command and delimited input processing. |
|
309 |
+ It is complex for several reasons: |
|
310 |
+ 1) FD commands are delivered on Unix domain sockets via recvnsg() on platforms which can do this. |
|
311 |
+ These command messages are accompanied by a single byte of data which is a NUL character. |
|
312 |
+ 2) Newline delimited commands are indicated by a command which is prefixed by an 'n' character. |
|
313 |
+ This character serves to indicate that the command will contain a newline which will cause |
|
314 |
+ command data to be read until the command input buffer is full or a newline is encountered. |
|
315 |
+ Once the delimiter is encountered, the data is returned without the prefixing 'n' byte. |
|
316 |
+ 3) Legacy clamd clients presented commands which may or may not have been delimited by a newline. |
|
317 |
+ If a command happens to be delimted by a newline, then only that command (and its newline) is |
|
318 |
+ read and passed back, otherwise, all data read (in a single read) which fits in the specified |
|
319 |
+ buffer will be returned. |
|
320 |
+*/ |
|
321 |
+int readsock(int sockfd, char *buf, size_t size, unsigned char delim, int timeout_sec, int force_delim, int read_command) |
|
311 | 322 |
{ |
312 | 323 |
int fd; |
313 | 324 |
ssize_t n; |
325 |
+ size_t boff = 0; |
|
326 |
+ char *pdelim; |
|
314 | 327 |
struct msghdr msg; |
315 | 328 |
struct iovec iov[1]; |
316 | 329 |
#ifdef HAVE_CONTROL_IN_MSGHDR |
... | ... |
@@ -323,45 +336,117 @@ int readsock(int sockfd, char *buf, size_t size) |
323 | 323 |
struct cmsghdr *cmsg; |
324 | 324 |
char tmp[CMSG_SPACE(sizeof(fd))]; |
325 | 325 |
#endif |
326 |
- |
|
327 |
- iov[0].iov_base = buf; |
|
328 |
- iov[0].iov_len = size; |
|
329 |
- memset(&msg, 0, sizeof(msg)); |
|
330 |
- msg.msg_iov = iov; |
|
331 |
- msg.msg_iovlen = 1; |
|
326 |
+ time_t starttime, timenow; |
|
327 |
+ |
|
328 |
+ time(&starttime); |
|
329 |
+ while(1) { |
|
330 |
+ time(&timenow); |
|
331 |
+ switch(poll_fd(sockfd, (timeout_sec && ((timeout_sec-(timenow-starttime)) > 0)) ? timeout_sec-(timenow-starttime) : 0)) { |
|
332 |
+ case 0: /* timeout */ |
|
333 |
+ return -2; |
|
334 |
+ case -1: |
|
335 |
+ if(errno == EINTR) |
|
336 |
+ continue; |
|
337 |
+ return -1; |
|
338 |
+ } |
|
339 |
+ break; |
|
340 |
+ } |
|
341 |
+ n = recv(sockfd, buf, size, MSG_PEEK); |
|
342 |
+ if(read_command) { |
|
343 |
+ if((n >= 1) && (buf[0] == 0)) { /* FD message */ |
|
344 |
+ iov[0].iov_base = buf; |
|
345 |
+ iov[0].iov_len = size; |
|
346 |
+ memset(&msg, 0, sizeof(msg)); |
|
347 |
+ msg.msg_iov = iov; |
|
348 |
+ msg.msg_iovlen = 1; |
|
332 | 349 |
#ifdef HAVE_ACCRIGHTS_IN_MSGHDR |
333 |
- msg.msg_accrights = (caddr_t)&fd; |
|
334 |
- msg.msg_accrightslen = sizeof(fd); |
|
350 |
+ msg.msg_accrights = (caddr_t)&fd; |
|
351 |
+ msg.msg_accrightslen = sizeof(fd); |
|
335 | 352 |
#endif |
336 | 353 |
#ifdef HAVE_CONTROL_IN_MSGHDR |
337 |
- msg.msg_control = tmp; |
|
338 |
- msg.msg_controllen = sizeof(tmp); |
|
354 |
+ msg.msg_control = tmp; |
|
355 |
+ msg.msg_controllen = sizeof(tmp); |
|
356 |
+#endif |
|
357 |
+#if defined(HAVE_RECVMSG) && !defined(C_OS2) && !defined(INCOMPLETE_CMSG) |
|
358 |
+ n = recvmsg(sockfd, &msg, 0); |
|
359 |
+#else |
|
360 |
+ n = recv(sockfd, buf, size, 0); |
|
339 | 361 |
#endif |
340 |
- fd = -1; |
|
341 |
- if ((n = recvmsg(sockfd, &msg, 0)) <= 0) |
|
342 |
- return n; |
|
343 |
- errno = EBADF; |
|
344 |
- if (n != 1 || buf[0] != 0) |
|
345 |
- return !strncmp(buf, CMD12, strlen(CMD12)) ? -1 : n; |
|
362 |
+ if (n <= 0) |
|
363 |
+ return n; |
|
364 |
+ errno = EBADF; |
|
346 | 365 |
#ifdef HAVE_ACCRIGHTS_IN_MSGHDR |
347 |
- if (msg.msg_accrightslen != sizeof(fd)) |
|
348 |
- return -1; |
|
366 |
+ if(msg.msg_accrightslen != sizeof(fd)) |
|
367 |
+ return -1; |
|
349 | 368 |
#endif |
350 | 369 |
#ifdef HAVE_CONTROL_IN_MSGHDR |
351 |
- cmsg = CMSG_FIRSTHDR(&msg); |
|
352 |
- if (cmsg == NULL) |
|
353 |
- return -1; |
|
354 |
- if (cmsg->cmsg_type != SCM_RIGHTS) |
|
355 |
- return -1; |
|
356 |
- if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) |
|
357 |
- return -1; |
|
358 |
- fd = *(int *)CMSG_DATA(cmsg); |
|
370 |
+ cmsg = CMSG_FIRSTHDR(&msg); |
|
371 |
+ if(cmsg == NULL) |
|
372 |
+ return -1; |
|
373 |
+ if(cmsg->cmsg_type != SCM_RIGHTS) |
|
374 |
+ return -1; |
|
375 |
+ if(cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) |
|
376 |
+ return -1; |
|
377 |
+ fd = *(int *)CMSG_DATA(cmsg); |
|
359 | 378 |
#endif |
360 |
- if (fd < 0) |
|
361 |
- return -1; |
|
362 |
- n = snprintf(buf, size, "FD %d", fd); |
|
363 |
- if (n >= size) |
|
364 |
- return -1; |
|
379 |
+ if(fd < 0) |
|
380 |
+ return -1; |
|
381 |
+ n = snprintf(buf, size, "FD %d", fd); |
|
382 |
+ if(n >= size) |
|
383 |
+ return -1; |
|
384 |
+ return n; |
|
385 |
+ } |
|
386 |
+ if((n >= 1) && (buf[0] == 'n')) { /* Newline delimited command */ |
|
387 |
+ force_delim = 1; |
|
388 |
+ delim = '\n'; |
|
389 |
+ } |
|
390 |
+ } |
|
391 |
+ while(boff < size) { |
|
392 |
+ if(force_delim) { |
|
393 |
+ pdelim = memchr(buf, delim, n+boff); |
|
394 |
+ if(pdelim) { |
|
395 |
+ n = recv(sockfd, buf+boff, pdelim-buf+1-boff, 0); |
|
396 |
+ break; |
|
397 |
+ } else { |
|
398 |
+ n = recv(sockfd, buf+boff, n, 0); |
|
399 |
+ if((boff+n) == size) |
|
400 |
+ break; |
|
401 |
+ boff += n; |
|
402 |
+ } |
|
403 |
+ } else { |
|
404 |
+ pdelim = memchr(buf, delim, n+boff); |
|
405 |
+ if(pdelim) |
|
406 |
+ n = recv(sockfd, buf+boff, pdelim-buf+1-boff, 0); |
|
407 |
+ else |
|
408 |
+ n = recv(sockfd, buf+boff, size-boff, 0); |
|
409 |
+ break; |
|
410 |
+ } |
|
411 |
+ while(1) { |
|
412 |
+ time(&timenow); |
|
413 |
+ switch(poll_fd(sockfd, ((timeout_sec-(timenow-starttime)) > 0) ? timeout_sec-(timenow-starttime) : 0)) { |
|
414 |
+ case 0: /* timeout */ |
|
415 |
+ return -2; |
|
416 |
+ case -1: |
|
417 |
+ if(errno == EINTR) |
|
418 |
+ continue; |
|
419 |
+ return -1; |
|
420 |
+ } |
|
421 |
+ break; |
|
422 |
+ } |
|
423 |
+ n = recv(sockfd, buf+boff, size-boff, MSG_PEEK); |
|
424 |
+ if(n < 0) |
|
425 |
+ return -1; |
|
426 |
+ if(n == 0) |
|
427 |
+ break; |
|
428 |
+ } |
|
429 |
+ n += boff; |
|
430 |
+ if(read_command) { |
|
431 |
+ if((n >= 1) && (buf[0] == 'n')) { /* Need to strip leading 'n' from command to attain standard command */ |
|
432 |
+ --n; |
|
433 |
+ memcpy(buf, buf+1, n); |
|
434 |
+ buf[n] = '\0'; |
|
435 |
+ } |
|
436 |
+ return !strncmp(buf, "FD", 2) ? -1 : n; /* an explicit FD command is invalid */ |
|
437 |
+ } |
|
365 | 438 |
return n; |
366 | 439 |
} |
367 |
-#endif |
... | ... |
@@ -33,10 +33,6 @@ int is_fd_connected(int fd); |
33 | 33 |
void virusaction(const char *filename, const char *virname, const struct cfgstruct *copt); |
34 | 34 |
int writen(int fd, void *buff, unsigned int count); |
35 | 35 |
|
36 |
-#if defined(HAVE_RECVMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR)) && !defined(C_CYGWIN) && !defined(C_OS2) && !defined(INCOMPLETE_CMSG) |
|
37 |
-int readsock(int sockfd, char *buf, size_t size); |
|
38 |
-#else |
|
39 |
-#define readsock read |
|
40 |
-#endif |
|
36 |
+int readsock(int sockfd, char *buf, size_t size, unsigned char delim, int timeout_sec, int force_delim, int read_command); |
|
41 | 37 |
|
42 | 38 |
#endif |
... | ... |
@@ -53,27 +53,14 @@ int command(int desc, const struct cl_node *root, const struct cl_limits *limits |
53 | 53 |
struct cfgstruct *cpt; |
54 | 54 |
|
55 | 55 |
|
56 |
- retval = poll_fd(desc, timeout); |
|
57 |
- switch (retval) { |
|
58 |
- case 0: /* timeout */ |
|
56 |
+ bread = readsock(desc, buff, sizeof(buff)-1, '\n', timeout, 0, 1); |
|
57 |
+ if(bread == -2) /* timeout */ |
|
59 | 58 |
return -2; |
60 |
- case -1: |
|
61 |
- mdprintf(desc, "ERROR\n"); |
|
62 |
- logg("!Command: poll_fd failed.\n"); |
|
63 |
- return -1; |
|
64 |
- } |
|
65 |
- |
|
66 |
- while((bread = readsock(desc, buff, 1024)) == -1 && errno == EINTR); |
|
67 |
- |
|
68 |
- if(bread == 0) { |
|
69 |
- /* Connection closed */ |
|
59 |
+ if(bread == 0) /* Connection closed */ |
|
70 | 60 |
return -1; |
71 |
- } |
|
72 |
- |
|
73 | 61 |
if(bread < 0) { |
74 |
- logg("!Command parser: read() failed.\n"); |
|
75 |
- /* at least try to display this error message */ |
|
76 |
- /* mdprintf(desc, "ERROR: Command parser: read() failed.\n"); */ |
|
62 |
+ mdprintf(desc, "ERROR\n"); |
|
63 |
+ logg("!Command: readsock() failed.\n"); |
|
77 | 64 |
return -1; |
78 | 65 |
} |
79 | 66 |
|
... | ... |
@@ -12,6 +12,8 @@ The daemon listens for incoming connections on Unix or TCP socket and scans file |
12 | 12 |
.SH "COMMANDS" |
13 | 13 |
.LP |
14 | 14 |
clamd recognizes the following commands: |
15 |
+ |
|
16 |
+Note: It's recommended to prefix clamd commands with the letter \fBn\fR (eg. nSCAN) to indicate that the command will be delimited by a newline character and that clamd should continue reading command data until a newline is read. The newline delimiter assures that the complete command and its entire argument will be processed as a single command. |
|
15 | 17 |
.TP |
16 | 18 |
\fBPING\fR |
17 | 19 |
Check the server's state. It should reply with "PONG". |