git-svn: trunk@4796
Török Edvin authored on 2009/02/17 03:26:58... | ... |
@@ -1,3 +1,9 @@ |
1 |
+Mon Feb 16 14:08:50 EET 2009 (edwin) |
|
2 |
+------------------------------------ |
|
3 |
+ * clamd/server-th.c, clamd/session.c, unit_tests/check_clamd.c: add |
|
4 |
+ more unit tests. Make handling of old-style commands compatible |
|
5 |
+ with old clamd: if they have a \n that will delimit the command. |
|
6 |
+ |
|
1 | 7 |
Mon Feb 16 17:59:57 CET 2009 (tk) |
2 | 8 |
--------------------------------- |
3 | 9 |
* libclamav, sigtool: fix handling of long signatures (bb#1395) |
... | ... |
@@ -240,6 +240,12 @@ static struct cl_engine *reload_db(struct cl_engine *engine, unsigned int dbopti |
240 | 240 |
return engine; |
241 | 241 |
} |
242 | 242 |
|
243 |
+/* |
|
244 |
+ * zCOMMANDS are delimited by \0 |
|
245 |
+ * nCOMMANDS are delimited by \n |
|
246 |
+ * Old-style non-prefixed commands are one packet, optionally delimited by \n, |
|
247 |
+ * with trailing \r|\n ignored |
|
248 |
+ */ |
|
243 | 249 |
static const char *get_cmd(struct fd_buf *buf, size_t off, size_t *len, char *term) |
244 | 250 |
{ |
245 | 251 |
unsigned char *pos; |
... | ... |
@@ -269,10 +275,18 @@ static const char *get_cmd(struct fd_buf *buf, size_t off, size_t *len, char *te |
269 | 269 |
return buf->buffer + off + 1; |
270 | 270 |
default: |
271 | 271 |
/* one packet = one command */ |
272 |
- *len = buf->off - off; |
|
273 |
- buf->buffer[buf->off] = '\0'; |
|
274 |
- cli_chomp(buf->buffer + off); |
|
275 |
- return buf->buffer + off; |
|
272 |
+ if (off) |
|
273 |
+ return NULL; |
|
274 |
+ pos = memchr(buf->buffer, '\n', buf->off); |
|
275 |
+ if (pos) { |
|
276 |
+ *len = pos - buf->buffer; |
|
277 |
+ *pos = '\0'; |
|
278 |
+ } else { |
|
279 |
+ *len = buf->off; |
|
280 |
+ buf->buffer[buf->off] = '\0'; |
|
281 |
+ } |
|
282 |
+ cli_chomp(buf->buffer); |
|
283 |
+ return buf->buffer; |
|
276 | 284 |
} |
277 | 285 |
} |
278 | 286 |
|
... | ... |
@@ -832,6 +846,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
832 | 832 |
conn.quota = buf->quota; |
833 | 833 |
conn.filename = buf->dumpname; |
834 | 834 |
conn.mode = buf->mode; |
835 |
+ conn.term = buf->term; |
|
835 | 836 |
/* Parse & dispatch commands */ |
836 | 837 |
while ((conn.mode == MODE_COMMAND) && |
837 | 838 |
(cmd = get_cmd(buf, pos, &cmdlen, &term)) != NULL) { |
... | ... |
@@ -980,8 +995,9 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi |
980 | 980 |
if (buf->chunksize > buf->quota) { |
981 | 981 |
logg("^INSTREAM: Size limit reached, (requested: %lu, max: %lu)\n", |
982 | 982 |
(unsigned long)buf->chunksize, (unsigned long)buf->quota); |
983 |
- conn_reply_error(&conn, "INSTREAM size limit exceeded. ERROR"); |
|
983 |
+ conn_reply_error(&conn, "INSTREAM size limit exceeded."); |
|
984 | 984 |
error = 1; |
985 |
+ break; |
|
985 | 986 |
} else { |
986 | 987 |
buf->quota -= buf->chunksize; |
987 | 988 |
} |
... | ... |
@@ -227,7 +227,7 @@ int command(client_conn_t *conn, int *virus) |
227 | 227 |
/* callback freed it */ |
228 | 228 |
conn->filename = NULL; |
229 | 229 |
*virus = scandata.infected; |
230 |
- return 0; |
|
230 |
+ return scandata.errors > 0 ? scandata.errors : 0; |
|
231 | 231 |
case COMMAND_FILDES: |
232 | 232 |
thrmgr_setactivetask(NULL, "FILDES"); |
233 | 233 |
#ifdef HAVE_FD_PASSING |
... | ... |
@@ -56,16 +56,77 @@ static void conn_teardown(void) |
56 | 56 |
#define SCANFILE BUILDDIR"/../test/clam.exe" |
57 | 57 |
#define FOUNDREPLY SCANFILE": ClamAV-Test-File.UNOFFICIAL FOUND" |
58 | 58 |
|
59 |
+/* some clean file */ |
|
60 |
+#define CLEANFILE SRCDIR"/Makefile.am" |
|
61 |
+#define CLEANREPLY CLEANFILE": OK" |
|
62 |
+#define UNKNOWN_REPLY "UNKNOWN COMMAND" |
|
63 |
+ |
|
64 |
+#define NONEXISTENT "/nonexistent\vfilename" |
|
65 |
+#define NONEXISTENT_REPLY NONEXISTENT": lstat() failed: No such file or directory. ERROR" |
|
66 |
+ |
|
67 |
+#define ACCDENIED BUILDDIR"/accdenied" |
|
68 |
+#define ACCDENIED_REPLY ACCDENIED": Access denied. ERROR" |
|
69 |
+ |
|
70 |
+static void commands_setup(void) |
|
71 |
+{ |
|
72 |
+ const char *nonempty = "NONEMPTYFILE"; |
|
73 |
+ int fd = open(NONEXISTENT, O_RDONLY); |
|
74 |
+ int rc; |
|
75 |
+ if (fd != -1) close(fd); |
|
76 |
+ fail_unless(fd == -1, "Nonexistent file exists!\n"); |
|
77 |
+ |
|
78 |
+ fd = open(ACCDENIED, O_CREAT | O_WRONLY, S_IWUSR); |
|
79 |
+ fail_unless_fmt(fd != -1, |
|
80 |
+ "Failed to create file for access denied tests: %s\n", strerror(errno)); |
|
81 |
+ |
|
82 |
+ |
|
83 |
+ fail_unless_fmt(fchmod(fd, S_IWUSR) != -1, |
|
84 |
+ "Failed to chmod: %s\n", strerror(errno)); |
|
85 |
+ /* must not be empty file */ |
|
86 |
+ fail_unless_fmt(write(fd, nonempty, strlen(nonempty)) == strlen(nonempty), |
|
87 |
+ "Failed to write into testfile: %s\n", strerror(errno)); |
|
88 |
+ close(fd); |
|
89 |
+} |
|
90 |
+ |
|
91 |
+static void commands_teardown(void) |
|
92 |
+{ |
|
93 |
+ int rc = unlink(ACCDENIED); |
|
94 |
+ fail_unless_fmt(rc != -1, "Failed to unlink access denied testfile: %s\n", strerror(errno)); |
|
95 |
+} |
|
96 |
+ |
|
59 | 97 |
static struct basic_test { |
60 | 98 |
const char *command; |
99 |
+ const char *extra; |
|
61 | 100 |
const char *reply; |
62 | 101 |
} basic_tests[] = { |
63 |
- {"PING", "PONG"}, |
|
64 |
- {"RELOAD","RELOADING"}, |
|
65 |
- {"VERSION", "ClamAV "REPO_VERSION""VERSION_SUFFIX}, |
|
66 |
- {"SCAN "SCANFILE, FOUNDREPLY}, |
|
67 |
- {"CONTSCAN "SCANFILE, FOUNDREPLY}, |
|
68 |
- {"MULTISCAN "SCANFILE, FOUNDREPLY}, |
|
102 |
+ {"PING", NULL, "PONG"}, |
|
103 |
+ {"RELOAD", NULL, "RELOADING"}, |
|
104 |
+ {"VERSION", NULL, "ClamAV "REPO_VERSION""VERSION_SUFFIX}, |
|
105 |
+ {"SCAN "SCANFILE, NULL, FOUNDREPLY}, |
|
106 |
+ {"SCAN "CLEANFILE, NULL, CLEANREPLY}, |
|
107 |
+ {"CONTSCAN "SCANFILE, NULL, FOUNDREPLY}, |
|
108 |
+ {"CONTSCAN "CLEANFILE, NULL, CLEANREPLY}, |
|
109 |
+ {"MULTISCAN "SCANFILE, NULL, FOUNDREPLY}, |
|
110 |
+ {"MULTISCAN "CLEANFILE, NULL, CLEANREPLY}, |
|
111 |
+ /* unknown commnads */ |
|
112 |
+ {"RANDOM", NULL, UNKNOWN_REPLY}, |
|
113 |
+ /* commands invalid as first */ |
|
114 |
+ {"END", NULL, UNKNOWN_REPLY}, |
|
115 |
+ /* commands for nonexistent files */ |
|
116 |
+ {"SCAN "NONEXISTENT, NULL, NONEXISTENT_REPLY}, |
|
117 |
+ {"CONTSCAN "NONEXISTENT, NULL, NONEXISTENT_REPLY}, |
|
118 |
+ {"MULTISCAN "NONEXISTENT, NULL, NONEXISTENT_REPLY}, |
|
119 |
+ /* commands for access denied files */ |
|
120 |
+ {"SCAN "ACCDENIED, NULL, ACCDENIED_REPLY}, |
|
121 |
+ {"CONTSCAN "ACCDENIED, NULL, ACCDENIED_REPLY}, |
|
122 |
+ {"MULTISCAN "ACCDENIED, NULL, ACCDENIED_REPLY}, |
|
123 |
+ /* commands with invalid/missing arguments */ |
|
124 |
+ {"SCAN", NULL, UNKNOWN_REPLY}, |
|
125 |
+ {"CONTSCAN", NULL, UNKNOWN_REPLY}, |
|
126 |
+ {"MULTISCAN", NULL, UNKNOWN_REPLY}, |
|
127 |
+ /* commands with invalid data */ |
|
128 |
+ {"INSTREAM", "\xff\xff\xff\xff", "INSTREAM size limit exceeded. ERROR"}, /* too big chunksize */ |
|
129 |
+ {"FILDES", "X", "No file descriptor received. ERROR"}, /* FILDES w/o ancillary data */ |
|
69 | 130 |
}; |
70 | 131 |
|
71 | 132 |
static void *recvfull(int sd, size_t *len) |
... | ... |
@@ -91,13 +152,18 @@ static void *recvfull(int sd, size_t *len) |
91 | 91 |
return buf; |
92 | 92 |
} |
93 | 93 |
|
94 |
-static void test_command(const char *cmd, size_t len, const char *expect, size_t expect_len) |
|
94 |
+static void test_command(const char *cmd, size_t len, const char *extra, const char *expect, size_t expect_len) |
|
95 | 95 |
{ |
96 | 96 |
void *recvdata; |
97 | 97 |
ssize_t rc; |
98 | 98 |
rc = send(sockd, cmd, len, 0); |
99 | 99 |
fail_unless_fmt((size_t)rc == len, "Unable to send(): %s\n", strerror(errno)); |
100 | 100 |
|
101 |
+ if (extra) { |
|
102 |
+ rc = send(sockd, extra, strlen(extra), 0); |
|
103 |
+ fail_unless_fmt((size_t)rc == strlen(extra), "Unable to send(): %s\n", strerror(errno)); |
|
104 |
+ } |
|
105 |
+ |
|
101 | 106 |
recvdata = recvfull(sockd, &len); |
102 | 107 |
|
103 | 108 |
fail_unless_fmt(len == expect_len, "Reply has wrong size: %lu, expected %lu, reply: %s\n", |
... | ... |
@@ -112,22 +178,46 @@ START_TEST (test_basic_commands) |
112 | 112 |
{ |
113 | 113 |
struct basic_test *test = &basic_tests[_i]; |
114 | 114 |
char nsend[BUFSIZ], nreply[BUFSIZ]; |
115 |
- /* send the command the "old way" */ |
|
116 |
- conn_setup(); |
|
117 |
- snprintf(nreply, sizeof(nreply), "%s\n", test->reply); |
|
118 |
- test_command(test->command, strlen(test->command), nreply, strlen(nreply)); |
|
119 |
- conn_teardown(); |
|
120 | 115 |
|
121 | 116 |
/* send nCOMMAND */ |
122 |
- conn_setup(); |
|
117 |
+ snprintf(nreply, sizeof(nreply), "%s\n", test->reply); |
|
123 | 118 |
snprintf(nsend, sizeof(nsend), "n%s\n", test->command); |
124 |
- test_command(nsend, strlen(nsend), nreply, strlen(nreply)); |
|
119 |
+ conn_setup(); |
|
120 |
+ test_command(nsend, strlen(nsend), test->extra, nreply, strlen(nreply)); |
|
125 | 121 |
conn_teardown(); |
126 | 122 |
|
127 | 123 |
/* send zCOMMAND */ |
128 |
- conn_setup(); |
|
129 | 124 |
snprintf(nsend, sizeof(nsend), "z%s", test->command); |
130 |
- test_command(nsend, strlen(nsend)+1, test->reply, strlen(test->reply)+1); |
|
125 |
+ conn_setup(); |
|
126 |
+ test_command(nsend, strlen(nsend)+1, test->extra, test->reply, strlen(test->reply)+1); |
|
127 |
+ conn_teardown(); |
|
128 |
+} |
|
129 |
+END_TEST |
|
130 |
+ |
|
131 |
+START_TEST (test_compat_commands) |
|
132 |
+{ |
|
133 |
+ /* test sending the command the "old way" */ |
|
134 |
+ struct basic_test *test = &basic_tests[_i]; |
|
135 |
+ char nsend[BUFSIZ], nreply[BUFSIZ]; |
|
136 |
+ |
|
137 |
+ snprintf(nreply, sizeof(nreply), "%s\n", test->reply); |
|
138 |
+ if (!test->extra) { |
|
139 |
+ /* one command = one packet, no delimiter */ |
|
140 |
+ conn_setup(); |
|
141 |
+ test_command(test->command, strlen(test->command), test->extra, nreply, strlen(nreply)); |
|
142 |
+ conn_teardown(); |
|
143 |
+ } |
|
144 |
+ |
|
145 |
+ /* one packet, \n delimited command, followed by "extra" if needed */ |
|
146 |
+ snprintf(nsend, sizeof(nsend), "%s\n", test->command); |
|
147 |
+ conn_setup(); |
|
148 |
+ test_command(nsend, strlen(nsend), test->extra, nreply, strlen(nreply)); |
|
149 |
+ conn_teardown(); |
|
150 |
+ |
|
151 |
+ /* one packet, \r\n delimited command, followed by "extra" if needed */ |
|
152 |
+ snprintf(nsend, sizeof(nsend), "%s\r\n", test->command); |
|
153 |
+ conn_setup(); |
|
154 |
+ test_command(nsend, strlen(nsend), test->extra, nreply, strlen(nreply)); |
|
131 | 155 |
conn_teardown(); |
132 | 156 |
} |
133 | 157 |
END_TEST |
... | ... |
@@ -204,12 +294,15 @@ START_TEST (tc_instream) |
204 | 204 |
} |
205 | 205 |
END_TEST |
206 | 206 |
|
207 |
+ |
|
207 | 208 |
static Suite *test_clamd_suite(void) |
208 | 209 |
{ |
209 | 210 |
Suite *s = suite_create("clamd"); |
210 | 211 |
TCase *tc_commands = tcase_create("clamd commands"); |
211 | 212 |
suite_add_tcase(s, tc_commands); |
213 |
+ tcase_add_unchecked_fixture(tc_commands, commands_setup, commands_teardown); |
|
212 | 214 |
tcase_add_loop_test(tc_commands, test_basic_commands, 0, sizeof(basic_tests)/sizeof(basic_tests[0])); |
215 |
+ tcase_add_loop_test(tc_commands, test_compat_commands, 0, sizeof(basic_tests)/sizeof(basic_tests[0])); |
|
213 | 216 |
tcase_add_test(tc_commands, tc_instream); |
214 | 217 |
tcase_add_test(tc_commands, tc_stats); |
215 | 218 |
return s; |