add support for buffered commands

git-svn: trunk@2233
remotes/push_mirror/metadata
Tomasz Kojm 19 years ago
parent ca73ba4a04
commit 442684f8bf
  1. 8
      clamav-devel/ChangeLog
  2. 159
      clamav-devel/clamd/others.c
  3. 6
      clamav-devel/clamd/others.h
  4. 1
      clamav-devel/clamd/scanner.c
  5. 23
      clamav-devel/clamd/session.c
  6. 2
      clamav-devel/docs/man/clamd.8.in

@ -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 <clamav-devel*subscriptions.pizzolato.net>
Mon Sep 4 21:06:52 CEST 2006 (tk)
----------------------------------
* libclamav/unrar/unrarvm.c: fix possible crash reported by Sven

@ -303,14 +303,27 @@ int writen(int fd, void *buff, unsigned int count)
return count;
}
/* Submitted by Richard Lyons <frob-clamav*webcentral.com.au> */
#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 <frob-clamav*webcentral.com.au> */
/*
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

@ -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

@ -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;
}

@ -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;
}

@ -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".

Loading…
Cancel
Save