Browse code

add support for buffered commands

git-svn: trunk@2233

Tomasz Kojm authored on 2006/09/05 07:30:09
Showing 6 changed files
... ...
@@ -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
... ...
@@ -253,6 +253,7 @@ int scan(const char *filename, unsigned long int *scanned, const struct cl_node
253 253
     if(!ret)
254 254
 	mdprintf(odesc, "%s: OK\n", filename);
255 255
 
256
+    mdprintf(odesc, "\n"); /* Terminate response with a blank line boundary */
256 257
     return ret;
257 258
 }
258 259
 
... ...
@@ -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".