diff --git a/clamav-devel/ChangeLog b/clamav-devel/ChangeLog index 2bbb18f7a..d7dd64a1a 100644 --- a/clamav-devel/ChangeLog +++ b/clamav-devel/ChangeLog @@ -1,3 +1,11 @@ +Tue Sep 5 00:23:26 CEST 2006 (tk) +---------------------------------- + * clamd: all commands can be now prefixed with the letter 'n' (eg. nSCAN) to + to indicate that they will be delimited by a new line character + (which assures that the complete command and its entire argument + will be processed as a single command) + Patch from Mark Pizzolato + Mon Sep 4 21:06:52 CEST 2006 (tk) ---------------------------------- * libclamav/unrar/unrarvm.c: fix possible crash reported by Sven diff --git a/clamav-devel/clamd/others.c b/clamav-devel/clamd/others.c index 22d85c2d1..7f94e7c93 100644 --- a/clamav-devel/clamd/others.c +++ b/clamav-devel/clamd/others.c @@ -303,14 +303,27 @@ int writen(int fd, void *buff, unsigned int count) return count; } -/* Submitted by Richard Lyons */ - -#if defined(HAVE_RECVMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR)) && !defined(C_CYGWIN) && !defined(C_OS2) && !defined(INCOMPLETE_CMSG) - -int readsock(int sockfd, char *buf, size_t size) +/* FD Support Submitted by Richard Lyons */ +/* + This procedure does timed clamd command and delimited input processing. + It is complex for several reasons: + 1) FD commands are delivered on Unix domain sockets via recvnsg() on platforms which can do this. + These command messages are accompanied by a single byte of data which is a NUL character. + 2) Newline delimited commands are indicated by a command which is prefixed by an 'n' character. + This character serves to indicate that the command will contain a newline which will cause + command data to be read until the command input buffer is full or a newline is encountered. + Once the delimiter is encountered, the data is returned without the prefixing 'n' byte. + 3) Legacy clamd clients presented commands which may or may not have been delimited by a newline. + If a command happens to be delimted by a newline, then only that command (and its newline) is + read and passed back, otherwise, all data read (in a single read) which fits in the specified + buffer will be returned. +*/ +int readsock(int sockfd, char *buf, size_t size, unsigned char delim, int timeout_sec, int force_delim, int read_command) { int fd; ssize_t n; + size_t boff = 0; + char *pdelim; struct msghdr msg; struct iovec iov[1]; #ifdef HAVE_CONTROL_IN_MSGHDR @@ -323,45 +336,117 @@ int readsock(int sockfd, char *buf, size_t size) struct cmsghdr *cmsg; char tmp[CMSG_SPACE(sizeof(fd))]; #endif - - iov[0].iov_base = buf; - iov[0].iov_len = size; - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = iov; - msg.msg_iovlen = 1; + time_t starttime, timenow; + + time(&starttime); + while(1) { + time(&timenow); + switch(poll_fd(sockfd, (timeout_sec && ((timeout_sec-(timenow-starttime)) > 0)) ? timeout_sec-(timenow-starttime) : 0)) { + case 0: /* timeout */ + return -2; + case -1: + if(errno == EINTR) + continue; + return -1; + } + break; + } + n = recv(sockfd, buf, size, MSG_PEEK); + if(read_command) { + if((n >= 1) && (buf[0] == 0)) { /* FD message */ + iov[0].iov_base = buf; + iov[0].iov_len = size; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 1; #ifdef HAVE_ACCRIGHTS_IN_MSGHDR - msg.msg_accrights = (caddr_t)&fd; - msg.msg_accrightslen = sizeof(fd); + msg.msg_accrights = (caddr_t)&fd; + msg.msg_accrightslen = sizeof(fd); #endif #ifdef HAVE_CONTROL_IN_MSGHDR - msg.msg_control = tmp; - msg.msg_controllen = sizeof(tmp); + msg.msg_control = tmp; + msg.msg_controllen = sizeof(tmp); +#endif +#if defined(HAVE_RECVMSG) && !defined(C_OS2) && !defined(INCOMPLETE_CMSG) + n = recvmsg(sockfd, &msg, 0); +#else + n = recv(sockfd, buf, size, 0); #endif - fd = -1; - if ((n = recvmsg(sockfd, &msg, 0)) <= 0) - return n; - errno = EBADF; - if (n != 1 || buf[0] != 0) - return !strncmp(buf, CMD12, strlen(CMD12)) ? -1 : n; + if (n <= 0) + return n; + errno = EBADF; #ifdef HAVE_ACCRIGHTS_IN_MSGHDR - if (msg.msg_accrightslen != sizeof(fd)) - return -1; + if(msg.msg_accrightslen != sizeof(fd)) + return -1; #endif #ifdef HAVE_CONTROL_IN_MSGHDR - cmsg = CMSG_FIRSTHDR(&msg); - if (cmsg == NULL) - return -1; - if (cmsg->cmsg_type != SCM_RIGHTS) - return -1; - if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) - return -1; - fd = *(int *)CMSG_DATA(cmsg); + cmsg = CMSG_FIRSTHDR(&msg); + if(cmsg == NULL) + return -1; + if(cmsg->cmsg_type != SCM_RIGHTS) + return -1; + if(cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) + return -1; + fd = *(int *)CMSG_DATA(cmsg); #endif - if (fd < 0) - return -1; - n = snprintf(buf, size, "FD %d", fd); - if (n >= size) - return -1; + if(fd < 0) + return -1; + n = snprintf(buf, size, "FD %d", fd); + if(n >= size) + return -1; + return n; + } + if((n >= 1) && (buf[0] == 'n')) { /* Newline delimited command */ + force_delim = 1; + delim = '\n'; + } + } + while(boff < size) { + if(force_delim) { + pdelim = memchr(buf, delim, n+boff); + if(pdelim) { + n = recv(sockfd, buf+boff, pdelim-buf+1-boff, 0); + break; + } else { + n = recv(sockfd, buf+boff, n, 0); + if((boff+n) == size) + break; + boff += n; + } + } else { + pdelim = memchr(buf, delim, n+boff); + if(pdelim) + n = recv(sockfd, buf+boff, pdelim-buf+1-boff, 0); + else + n = recv(sockfd, buf+boff, size-boff, 0); + break; + } + while(1) { + time(&timenow); + switch(poll_fd(sockfd, ((timeout_sec-(timenow-starttime)) > 0) ? timeout_sec-(timenow-starttime) : 0)) { + case 0: /* timeout */ + return -2; + case -1: + if(errno == EINTR) + continue; + return -1; + } + break; + } + n = recv(sockfd, buf+boff, size-boff, MSG_PEEK); + if(n < 0) + return -1; + if(n == 0) + break; + } + n += boff; + if(read_command) { + if((n >= 1) && (buf[0] == 'n')) { /* Need to strip leading 'n' from command to attain standard command */ + --n; + memcpy(buf, buf+1, n); + buf[n] = '\0'; + } + return !strncmp(buf, "FD", 2) ? -1 : n; /* an explicit FD command is invalid */ + } return n; } -#endif diff --git a/clamav-devel/clamd/others.h b/clamav-devel/clamd/others.h index 30cbc4508..4222e4e43 100644 --- a/clamav-devel/clamd/others.h +++ b/clamav-devel/clamd/others.h @@ -33,10 +33,6 @@ int is_fd_connected(int fd); void virusaction(const char *filename, const char *virname, const struct cfgstruct *copt); int writen(int fd, void *buff, unsigned int count); -#if defined(HAVE_RECVMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR)) && !defined(C_CYGWIN) && !defined(C_OS2) && !defined(INCOMPLETE_CMSG) -int readsock(int sockfd, char *buf, size_t size); -#else -#define readsock read -#endif +int readsock(int sockfd, char *buf, size_t size, unsigned char delim, int timeout_sec, int force_delim, int read_command); #endif diff --git a/clamav-devel/clamd/scanner.c b/clamav-devel/clamd/scanner.c index 989c9ba1f..346adca38 100644 --- a/clamav-devel/clamd/scanner.c +++ b/clamav-devel/clamd/scanner.c @@ -253,6 +253,7 @@ int scan(const char *filename, unsigned long int *scanned, const struct cl_node if(!ret) mdprintf(odesc, "%s: OK\n", filename); + mdprintf(odesc, "\n"); /* Terminate response with a blank line boundary */ return ret; } diff --git a/clamav-devel/clamd/session.c b/clamav-devel/clamd/session.c index 7e23d56a5..984cd4d53 100644 --- a/clamav-devel/clamd/session.c +++ b/clamav-devel/clamd/session.c @@ -53,27 +53,14 @@ int command(int desc, const struct cl_node *root, const struct cl_limits *limits struct cfgstruct *cpt; - retval = poll_fd(desc, timeout); - switch (retval) { - case 0: /* timeout */ + bread = readsock(desc, buff, sizeof(buff)-1, '\n', timeout, 0, 1); + if(bread == -2) /* timeout */ return -2; - case -1: - mdprintf(desc, "ERROR\n"); - logg("!Command: poll_fd failed.\n"); - return -1; - } - - while((bread = readsock(desc, buff, 1024)) == -1 && errno == EINTR); - - if(bread == 0) { - /* Connection closed */ + if(bread == 0) /* Connection closed */ return -1; - } - if(bread < 0) { - logg("!Command parser: read() failed.\n"); - /* at least try to display this error message */ - /* mdprintf(desc, "ERROR: Command parser: read() failed.\n"); */ + mdprintf(desc, "ERROR\n"); + logg("!Command: readsock() failed.\n"); return -1; } diff --git a/clamav-devel/docs/man/clamd.8.in b/clamav-devel/docs/man/clamd.8.in index 9f82ad3d6..f8c09d4cd 100644 --- a/clamav-devel/docs/man/clamd.8.in +++ b/clamav-devel/docs/man/clamd.8.in @@ -12,6 +12,8 @@ The daemon listens for incoming connections on Unix or TCP socket and scans file .SH "COMMANDS" .LP clamd recognizes the following commands: + +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. .TP \fBPING\fR Check the server's state. It should reply with "PONG".