git-svn: trunk@5049
Török Edvin authored on 2009/04/20 23:26:48... | ... |
@@ -1,3 +1,8 @@ |
1 |
+Mon Apr 20 17:20:27 EEST 2009 (edwin) |
|
2 |
+------------------------------------- |
|
3 |
+ * clamd/server-th.c, unit_tests/check_clamd.c: Fix clamd INSTREAM |
|
4 |
+ handling inside IDSESSION (bb #1564). |
|
5 |
+ |
|
1 | 6 |
Fri Apr 17 18:23:44 CEST 2009 (acab) |
2 | 7 |
------------------------------------ |
3 | 8 |
* clamav-milter/clamav-milter.c: spam syslog with start events (bb#1557) |
... | ... |
@@ -641,7 +641,7 @@ static int handle_stream(client_conn_t *conn, struct fd_buf *buf, const struct o |
641 | 641 |
pos = 4; |
642 | 642 |
memmove (buf->buffer, &buf->buffer[pos], buf->off - pos); |
643 | 643 |
buf->off -= pos; |
644 |
- pos = 0; |
|
644 |
+ *ppos = 0; |
|
645 | 645 |
buf->id++; |
646 | 646 |
return 0; |
647 | 647 |
} |
... | ... |
@@ -651,6 +651,7 @@ static int handle_stream(client_conn_t *conn, struct fd_buf *buf, const struct o |
651 | 651 |
(unsigned long)buf->chunksize, (unsigned long)buf->quota); |
652 | 652 |
conn_reply_error(conn, "INSTREAM size limit exceeded."); |
653 | 653 |
*error = 1; |
654 |
+ *ppos = pos; |
|
654 | 655 |
return -1; |
655 | 656 |
} else { |
656 | 657 |
buf->quota -= buf->chunksize; |
... | ... |
@@ -153,42 +153,52 @@ static void commands_teardown(void) |
153 | 153 |
#define VERSION_REPLY "ClamAV "REPO_VERSION""VERSION_SUFFIX |
154 | 154 |
|
155 | 155 |
#define VCMDS_REPLY VERSION_REPLY"| COMMANDS: SCAN QUIT RELOAD PING CONTSCAN VERSIONCOMMANDS VERSION STREAM END SHUTDOWN MULTISCAN FILDES STATS IDSESSION INSTREAM" |
156 |
+ |
|
157 |
+enum idsession_support { |
|
158 |
+ IDS_OK, /* accepted */ |
|
159 |
+ IDS_REJECT, |
|
160 |
+ /* after sending this message, clamd will reply, then accept |
|
161 |
+ * no further commands, but still reply to all active commands */ |
|
162 |
+ IDS_END /* the END command */ |
|
163 |
+}; |
|
164 |
+ |
|
156 | 165 |
static struct basic_test { |
157 | 166 |
const char *command; |
158 | 167 |
const char *extra; |
159 | 168 |
const char *reply; |
160 | 169 |
int support_old; |
161 | 170 |
int skiproot; |
171 |
+ enum idsession_support ids; |
|
162 | 172 |
} basic_tests[] = { |
163 |
- {"PING", NULL, "PONG", 1, 0}, |
|
164 |
- {"RELOAD", NULL, "RELOADING", 1, 0}, |
|
165 |
- {"VERSION", NULL, VERSION_REPLY, 1, 0}, |
|
166 |
- {"VERSIONCOMMANDS", NULL, VCMDS_REPLY, 0, 0}, |
|
167 |
- {"SCAN "SCANFILE, NULL, FOUNDREPLY, 1, 0}, |
|
168 |
- {"SCAN "CLEANFILE, NULL, CLEANREPLY, 1, 0}, |
|
169 |
- {"CONTSCAN "SCANFILE, NULL, FOUNDREPLY, 1, 0}, |
|
170 |
- {"CONTSCAN "CLEANFILE, NULL, CLEANREPLY, 1, 0}, |
|
171 |
- {"MULTISCAN "SCANFILE, NULL, FOUNDREPLY, 1, 0}, |
|
172 |
- {"MULTISCAN "CLEANFILE, NULL, CLEANREPLY, 1, 0}, |
|
173 |
+ {"PING", NULL, "PONG", 1, 0, IDS_OK}, |
|
174 |
+ {"RELOAD", NULL, "RELOADING", 1, 0, IDS_REJECT}, |
|
175 |
+ {"VERSION", NULL, VERSION_REPLY, 1, 0, IDS_OK}, |
|
176 |
+ {"VERSIONCOMMANDS", NULL, VCMDS_REPLY, 0, 0, IDS_REJECT}, |
|
177 |
+ {"SCAN "SCANFILE, NULL, FOUNDREPLY, 1, 0, IDS_OK}, |
|
178 |
+ {"SCAN "CLEANFILE, NULL, CLEANREPLY, 1, 0, IDS_OK}, |
|
179 |
+ {"CONTSCAN "SCANFILE, NULL, FOUNDREPLY, 1, 0, IDS_REJECT}, |
|
180 |
+ {"CONTSCAN "CLEANFILE, NULL, CLEANREPLY, 1, 0, IDS_REJECT}, |
|
181 |
+ {"MULTISCAN "SCANFILE, NULL, FOUNDREPLY, 1, 0, IDS_REJECT}, |
|
182 |
+ {"MULTISCAN "CLEANFILE, NULL, CLEANREPLY, 1, 0, IDS_REJECT}, |
|
173 | 183 |
/* unknown commnads */ |
174 |
- {"RANDOM", NULL, UNKNOWN_REPLY, 1, 0}, |
|
184 |
+ {"RANDOM", NULL, UNKNOWN_REPLY, 1, 0, IDS_REJECT}, |
|
175 | 185 |
/* commands invalid as first */ |
176 |
- {"END", NULL, UNKNOWN_REPLY, 1, 0}, |
|
186 |
+ {"END", NULL, UNKNOWN_REPLY, 1, 0, IDS_END}, |
|
177 | 187 |
/* commands for nonexistent files */ |
178 |
- {"SCAN "NONEXISTENT, NULL, NONEXISTENT_REPLY, 1, 0}, |
|
179 |
- {"CONTSCAN "NONEXISTENT, NULL, NONEXISTENT_REPLY, 1, 0}, |
|
180 |
- {"MULTISCAN "NONEXISTENT, NULL, NONEXISTENT_REPLY, 1, 0}, |
|
188 |
+ {"SCAN "NONEXISTENT, NULL, NONEXISTENT_REPLY, 1, 0, IDS_OK}, |
|
189 |
+ {"CONTSCAN "NONEXISTENT, NULL, NONEXISTENT_REPLY, 1, 0, IDS_REJECT}, |
|
190 |
+ {"MULTISCAN "NONEXISTENT, NULL, NONEXISTENT_REPLY, 1, 0, IDS_REJECT}, |
|
181 | 191 |
/* commands for access denied files */ |
182 |
- {"SCAN "ACCDENIED, NULL, ACCDENIED_REPLY, 1, 1}, |
|
183 |
- {"CONTSCAN "ACCDENIED, NULL, ACCDENIED_REPLY, 1, 1}, |
|
184 |
- {"MULTISCAN "ACCDENIED, NULL, ACCDENIED_REPLY, 1, 1}, |
|
192 |
+ {"SCAN "ACCDENIED, NULL, ACCDENIED_REPLY, 1, 1, IDS_OK}, |
|
193 |
+ {"CONTSCAN "ACCDENIED, NULL, ACCDENIED_REPLY, 1, 1, IDS_REJECT}, |
|
194 |
+ {"MULTISCAN "ACCDENIED, NULL, ACCDENIED_REPLY, 1, 1, IDS_REJECT}, |
|
185 | 195 |
/* commands with invalid/missing arguments */ |
186 |
- {"SCAN", NULL, UNKNOWN_REPLY, 1, 0}, |
|
187 |
- {"CONTSCAN", NULL, UNKNOWN_REPLY, 1, 0}, |
|
188 |
- {"MULTISCAN", NULL, UNKNOWN_REPLY, 1, 0}, |
|
196 |
+ {"SCAN", NULL, UNKNOWN_REPLY, 1, 0, IDS_REJECT}, |
|
197 |
+ {"CONTSCAN", NULL, UNKNOWN_REPLY, 1, 0, IDS_REJECT}, |
|
198 |
+ {"MULTISCAN", NULL, UNKNOWN_REPLY, 1, 0, IDS_REJECT}, |
|
189 | 199 |
/* commands with invalid data */ |
190 |
- {"INSTREAM", "\xff\xff\xff\xff", "INSTREAM size limit exceeded. ERROR", 0, 0}, /* too big chunksize */ |
|
191 |
- {"FILDES", "X", "No file descriptor received. ERROR", 1, 0}, /* FILDES w/o ancillary data */ |
|
200 |
+ {"INSTREAM", "\xff\xff\xff\xff", "INSTREAM size limit exceeded. ERROR", 0, 0, IDS_REJECT}, /* too big chunksize */ |
|
201 |
+ {"FILDES", "X", "No file descriptor received. ERROR", 1, 0, IDS_REJECT}, /* FILDES w/o ancillary data */ |
|
192 | 202 |
}; |
193 | 203 |
|
194 | 204 |
static void *recvpartial(int sd, size_t *len, int partial) |
... | ... |
@@ -204,7 +214,6 @@ static void *recvpartial(int sd, size_t *len, int partial) |
204 | 204 |
buf = realloc(buf, *len); |
205 | 205 |
fail_unless(!!buf, "Cannot realloc buffer\n"); |
206 | 206 |
} |
207 |
- |
|
208 | 207 |
rc = recv(sd, buf + off, BUFSIZ, 0); |
209 | 208 |
fail_unless_fmt(rc != -1, "recv() failed: %s\n", strerror(errno)); |
210 | 209 |
off += rc; |
... | ... |
@@ -308,6 +317,7 @@ END_TEST |
308 | 308 |
#endif |
309 | 309 |
|
310 | 310 |
#define EXPECT_INSTREAM "stream: ClamAV-Test-File.UNOFFICIAL FOUND\n" |
311 |
+#define EXPECT_INSTREAM0 "stream: ClamAV-Test-File.UNOFFICIAL FOUND" |
|
311 | 312 |
|
312 | 313 |
#define STATS_REPLY "POOLS: 1\n\nSTATE: VALID PRIMARY\n" |
313 | 314 |
START_TEST (test_stats) |
... | ... |
@@ -335,16 +345,11 @@ START_TEST (test_stats) |
335 | 335 |
} |
336 | 336 |
END_TEST |
337 | 337 |
|
338 |
-START_TEST (test_instream) |
|
338 |
+static size_t prepare_instream(char *buf, size_t off, size_t buflen) |
|
339 | 339 |
{ |
340 |
- int fd, nread, rc; |
|
341 | 340 |
struct stat stbuf; |
341 |
+ int fd, nread; |
|
342 | 342 |
uint32_t chunk; |
343 |
- void *recvdata; |
|
344 |
- size_t len, expect_len; |
|
345 |
- char buf[4096] = "nINSTREAM\n"; |
|
346 |
- size_t off = strlen(buf); |
|
347 |
- |
|
348 | 343 |
fail_unless_fmt(stat(SCANFILE, &stbuf) != -1, "stat failed for %s: %s", SCANFILE, strerror(errno)); |
349 | 344 |
|
350 | 345 |
fd = open(SCANFILE, O_RDONLY); |
... | ... |
@@ -353,14 +358,26 @@ START_TEST (test_instream) |
353 | 353 |
chunk = htonl(stbuf.st_size); |
354 | 354 |
memcpy(&buf[off], &chunk, sizeof(chunk)); |
355 | 355 |
off += 4; |
356 |
- nread = read(fd, &buf[off], sizeof(buf)-off-4); |
|
357 |
- fail_unless_fmt(nread == stbuf.st_size, "read failed: %s\n", strerror(errno)); |
|
356 |
+ nread = read(fd, &buf[off], buflen-off-4); |
|
357 |
+ fail_unless_fmt(nread == stbuf.st_size, "read failed: %d != %d, %s\n", nread, stbuf.st_size, strerror(errno)); |
|
358 | 358 |
off += nread; |
359 | 359 |
buf[off++]=0; |
360 | 360 |
buf[off++]=0; |
361 | 361 |
buf[off++]=0; |
362 | 362 |
buf[off++]=0; |
363 | 363 |
close(fd); |
364 |
+ return off; |
|
365 |
+} |
|
366 |
+ |
|
367 |
+START_TEST (test_instream) |
|
368 |
+{ |
|
369 |
+ void *recvdata; |
|
370 |
+ size_t len, expect_len; |
|
371 |
+ char buf[4096] = "nINSTREAM\n"; |
|
372 |
+ size_t off = strlen(buf); |
|
373 |
+ int rc; |
|
374 |
+ |
|
375 |
+ off = prepare_instream(buf, off, sizeof(buf)); |
|
364 | 376 |
|
365 | 377 |
conn_setup(); |
366 | 378 |
fail_unless((size_t)send(sockd, buf, off, 0) == off, "send() failed: %s\n", strerror(errno)); |
... | ... |
@@ -713,6 +730,110 @@ START_TEST (test_stream) |
713 | 713 |
} |
714 | 714 |
END_TEST |
715 | 715 |
|
716 |
+#define END_CMD "zEND" |
|
717 |
+#define INSTREAM_CMD "zINSTREAM" |
|
718 |
+static void test_idsession_commands(int split, int instream) |
|
719 |
+{ |
|
720 |
+ char buf[20480]; |
|
721 |
+ size_t i, len=0, j=0; |
|
722 |
+ char *recvdata; |
|
723 |
+ char *p = buf; |
|
724 |
+ const char *replies[2 + sizeof(basic_tests)/sizeof(basic_tests[0])]; |
|
725 |
+ |
|
726 |
+ /* test all commands that must be accepted inside an IDSESSION */ |
|
727 |
+ for (i=0;i < sizeof(basic_tests)/sizeof(basic_tests[0]); i++) { |
|
728 |
+ const struct basic_test *test = &basic_tests[i]; |
|
729 |
+ if (test->ids == IDS_OK) { |
|
730 |
+ fail_unless(p+strlen(test->command)+2 < buf+sizeof(buf), "Buffer too small"); |
|
731 |
+ *p++ = 'z'; |
|
732 |
+ strcpy(p, test->command); |
|
733 |
+ p += strlen(test->command); |
|
734 |
+ *p++ = '\0'; |
|
735 |
+ if (test->extra) { |
|
736 |
+ fail_unless(p+strlen(test->extra) < buf+sizeof(buf), "Buffer too small"); |
|
737 |
+ strcpy(p, test->extra); |
|
738 |
+ p += strlen(test->extra); |
|
739 |
+ } |
|
740 |
+ replies[j++] = test->reply; |
|
741 |
+ } |
|
742 |
+ if (instream && test->ids == IDS_END) { |
|
743 |
+ uint32_t chunk; |
|
744 |
+ int fd; |
|
745 |
+ /* IDS_END - in middle of other commands, perfect for inserting |
|
746 |
+ * INSTREAM */ |
|
747 |
+ fail_unless(p+sizeof(INSTREAM_CMD)+544< buf+sizeof(buf), "Buffer too small"); |
|
748 |
+ memcpy(p, INSTREAM_CMD, sizeof(INSTREAM_CMD)); |
|
749 |
+ p += sizeof(INSTREAM_CMD); |
|
750 |
+ p += prepare_instream(p, 0, 552); |
|
751 |
+ replies[j++] = EXPECT_INSTREAM0; |
|
752 |
+ fail_unless(p+sizeof(INSTREAM_CMD)+16388< buf+sizeof(buf), "Buffer too small"); |
|
753 |
+ memcpy(p, INSTREAM_CMD, sizeof(INSTREAM_CMD)); |
|
754 |
+ p += sizeof(INSTREAM_CMD); |
|
755 |
+ chunk=htonl(16384); |
|
756 |
+ memcpy(p, &chunk, 4); |
|
757 |
+ p+=4; |
|
758 |
+ memset(p, 0x5a, 16384); |
|
759 |
+ p += 16384; |
|
760 |
+ *p++='\0'; |
|
761 |
+ *p++='\0'; |
|
762 |
+ *p++='\0'; |
|
763 |
+ *p++='\0'; |
|
764 |
+ replies[j++] = "stream: OK"; |
|
765 |
+ } |
|
766 |
+ } |
|
767 |
+ fail_unless(p+sizeof(END_CMD) < buf+sizeof(buf), "Buffer too small"); |
|
768 |
+ memcpy(p, END_CMD, sizeof(END_CMD)); |
|
769 |
+ p += sizeof(END_CMD); |
|
770 |
+ |
|
771 |
+ if (split) { |
|
772 |
+ /* test corner-cases: 1-byte sends */ |
|
773 |
+ for (i=0;i<p-buf;i++) |
|
774 |
+ fail_unless((size_t)send(sockd, &buf[i], 1, 0) == 1, "send() failed: %u, %s\n", i, strerror(errno)); |
|
775 |
+ } else { |
|
776 |
+ fail_unless((size_t)send(sockd, buf, p-buf, 0) == p-buf,"send() failed: %s\n", strerror(errno)); |
|
777 |
+ } |
|
778 |
+ recvdata = recvfull(sockd, &len); |
|
779 |
+ p = recvdata; |
|
780 |
+ for (i=0;i < sizeof(basic_tests)/sizeof(basic_tests[0]); i++) { |
|
781 |
+ const struct basic_test *test = &basic_tests[i]; |
|
782 |
+ if (test->ids == IDS_OK) { |
|
783 |
+ unsigned id; |
|
784 |
+ char *q = strchr(p, ':'); |
|
785 |
+ fail_unless_fmt(!!q, "No ID in reply: %s\n", p); |
|
786 |
+ *q = '\0'; |
|
787 |
+ fail_unless_fmt(sscanf(p, "%u", &id) == 1,"Wrong ID in reply: %s\n", p); |
|
788 |
+ fail_unless(id > 0, "ID cannot be zero"); |
|
789 |
+ fail_unless_fmt(id <= j, "ID too big: %u, max: %u\n", id, j); |
|
790 |
+ q += 2; |
|
791 |
+ fail_unless_fmt(!strcmp(q, replies[id-1]), |
|
792 |
+ "Wrong ID reply for ID %u: %s, expected %s\n", |
|
793 |
+ id, |
|
794 |
+ q, replies[id-1]); |
|
795 |
+ p = q + strlen(q)+1; |
|
796 |
+ } |
|
797 |
+ } |
|
798 |
+ free(recvdata); |
|
799 |
+ conn_teardown(); |
|
800 |
+} |
|
801 |
+ |
|
802 |
+#define ID_CMD "zIDSESSION" |
|
803 |
+START_TEST(test_idsession) |
|
804 |
+{ |
|
805 |
+ conn_setup(); |
|
806 |
+ fail_unless_fmt((size_t)send(sockd, ID_CMD, sizeof(ID_CMD), 0) == sizeof(ID_CMD), |
|
807 |
+ "send() failed: %s\n", strerror(errno)); |
|
808 |
+ test_idsession_commands(0, 0); |
|
809 |
+ conn_setup(); |
|
810 |
+ fail_unless_fmt((size_t)send(sockd, ID_CMD, sizeof(ID_CMD), 0) == sizeof(ID_CMD), |
|
811 |
+ "send() failed: %s\n", strerror(errno)); |
|
812 |
+ test_idsession_commands(1, 0); |
|
813 |
+ conn_setup(); |
|
814 |
+ fail_unless_fmt((size_t)send(sockd, ID_CMD, sizeof(ID_CMD), 0) == sizeof(ID_CMD), |
|
815 |
+ "send() failed: %s\n", strerror(errno)); |
|
816 |
+ test_idsession_commands(0, 1); |
|
817 |
+} |
|
818 |
+END_TEST |
|
819 |
+ |
|
716 | 820 |
static Suite *test_clamd_suite(void) |
717 | 821 |
{ |
718 | 822 |
Suite *s = suite_create("clamd"); |
... | ... |
@@ -728,6 +849,7 @@ static Suite *test_clamd_suite(void) |
728 | 728 |
tcase_add_test(tc_commands, test_stats); |
729 | 729 |
tcase_add_test(tc_commands, test_instream); |
730 | 730 |
tcase_add_test(tc_commands, test_stream); |
731 |
+ tcase_add_test(tc_commands, test_idsession); |
|
731 | 732 |
tc_stress = tcase_create("clamd stress test"); |
732 | 733 |
suite_add_tcase(s, tc_stress); |
733 | 734 |
tcase_set_timeout(tc_stress, 20); |