git-svn-id: file:///var/lib/svn/clamav-devel/branches/clamd-proto@4677 77e5149b-7576-45b1-b177-96237e5ba77b
aCaB authored on 2009/02/05 02:18:58... | ... |
@@ -30,6 +30,7 @@ |
30 | 30 |
#ifdef HAVE_SYS_LIMITS_H |
31 | 31 |
#include <sys/limits.h> |
32 | 32 |
#endif |
33 |
+#include <sys/select.h> |
|
33 | 34 |
#include <sys/un.h> |
34 | 35 |
#include <netinet/in.h> |
35 | 36 |
#include <arpa/inet.h> |
... | ... |
@@ -62,7 +63,6 @@ static struct sockaddr *mainsa = NULL; |
62 | 62 |
static int mainsasz; |
63 | 63 |
static struct sockaddr_un nixsock; |
64 | 64 |
static struct sockaddr_in tcpsock; |
65 |
-static struct sockaddr_in strmsock; |
|
66 | 65 |
enum { |
67 | 66 |
CONT, |
68 | 67 |
MULTI, |
... | ... |
@@ -177,12 +177,75 @@ static int recvln(struct RCVLN *s, char **rbol, char **reol) { |
177 | 177 |
} |
178 | 178 |
} |
179 | 179 |
|
180 |
+static int send_stream(int sockd, const char *filename) { |
|
181 |
+ uint32_t buf[BUFSIZ/sizeof(uint32_t)]; |
|
182 |
+ int fd, len; |
|
183 |
+ |
|
184 |
+ if(filename) { |
|
185 |
+ if(!(fd = open(filename, O_RDONLY))) { |
|
186 |
+ logg("!Open failed on %s.\n", filename); |
|
187 |
+ return 1; |
|
188 |
+ } |
|
189 |
+ } else fd = 0; |
|
190 |
+ |
|
191 |
+ if(sendln(sockd, "zINSTREAM", 10)) return 1; |
|
192 |
+ |
|
193 |
+ while((len = read(fd, &buf[1], sizeof(buf) - sizeof(uint32_t))) > 0) { |
|
194 |
+ buf[0] = htonl(len); |
|
195 |
+ if(sendln(sockd, (const char *)buf, len+sizeof(uint32_t))) { /* FIXME: conn might be closed unexpectedly due to limits */ |
|
196 |
+ logg("!Can't write to the socket.\n"); |
|
197 |
+ close(fd); |
|
198 |
+ return 1; |
|
199 |
+ } |
|
200 |
+ } |
|
201 |
+ close(fd); |
|
202 |
+ if(len) { |
|
203 |
+ logg("!Failed to read from %s.\n", filename); |
|
204 |
+ return 1; |
|
205 |
+ } |
|
206 |
+ return 0; |
|
207 |
+} |
|
208 |
+ |
|
209 |
+static int send_fdpass(int sockd, const char *filename) { |
|
210 |
+ struct iovec iov[1]; |
|
211 |
+ struct msghdr msg; |
|
212 |
+ struct cmsghdr *cmsg; |
|
213 |
+ unsigned char fdbuf[CMSG_SPACE(sizeof(int))]; |
|
214 |
+ char dummy[]=""; |
|
215 |
+ int fd; |
|
216 |
+ |
|
217 |
+ if(filename) { |
|
218 |
+ if(!(fd = open(filename, O_RDONLY))) { |
|
219 |
+ logg("!Open failed on %s.\n", filename); |
|
220 |
+ return 1; |
|
221 |
+ } |
|
222 |
+ } else fd = 0; |
|
223 |
+ if(sendln(sockd, "zFILDES", 8)) return 1; |
|
224 |
+ |
|
225 |
+ iov[0].iov_base = dummy; |
|
226 |
+ iov[0].iov_len = 1; |
|
227 |
+ memset(&msg, 0, sizeof(msg)); |
|
228 |
+ msg.msg_control = fdbuf; |
|
229 |
+ msg.msg_iov = iov; |
|
230 |
+ msg.msg_iovlen = 1; |
|
231 |
+ msg.msg_controllen = CMSG_LEN(sizeof(int)); |
|
232 |
+ cmsg = CMSG_FIRSTHDR(&msg); |
|
233 |
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
|
234 |
+ cmsg->cmsg_level = SOL_SOCKET; |
|
235 |
+ cmsg->cmsg_type = SCM_RIGHTS; |
|
236 |
+ *(int *)CMSG_DATA(cmsg) = fd; |
|
237 |
+ if(sendmsg(sockd, &msg, 0) == -1) { |
|
238 |
+ logg("!FD send failed\n"); |
|
239 |
+ return 1; |
|
240 |
+ } |
|
241 |
+ return 0; |
|
242 |
+} |
|
243 |
+ |
|
180 | 244 |
static int dsresult(int sockd, int scantype, const char *filename) |
181 | 245 |
{ |
182 |
- int infected = 0, waserror = 0, fd; |
|
246 |
+ int infected = 0, waserror = 0; |
|
183 | 247 |
int len; |
184 | 248 |
char *bol, *eol; |
185 |
- char buf[BUFSIZ]; |
|
186 | 249 |
struct RCVLN rcv; |
187 | 250 |
|
188 | 251 |
recvlninit(&rcv, sockd); |
... | ... |
@@ -201,82 +264,14 @@ static int dsresult(int sockd, int scantype, const char *filename) |
201 | 201 |
break; |
202 | 202 |
|
203 | 203 |
case STREAM: |
204 |
- { |
|
205 |
- int wsockd; |
|
206 |
- |
|
207 |
- if(filename) { |
|
208 |
- if(!(fd = open(filename, O_RDONLY))) { |
|
209 |
- logg("!Open failed on %s.\n", filename); |
|
210 |
- return -1; |
|
211 |
- } |
|
212 |
- } else fd = 0; |
|
213 |
- if(sendln(sockd, "zSTREAM", 8)) return -1; |
|
214 |
- if(!(len = recvln(&rcv, &bol, &eol)) || len < 7 || memcmp(bol, "PORT ", 5) || !(len = atoi(bol + 5))) return -1; |
|
215 |
- strmsock.sin_port = htons(len); |
|
216 |
- if((wsockd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { |
|
217 |
- perror("socket()"); |
|
218 |
- logg("!Can't create the stream socket.\n"); |
|
219 |
- close(fd); |
|
220 |
- return -1; |
|
221 |
- } |
|
222 |
- if(connect(wsockd, (struct sockaddr *)&strmsock, sizeof(strmsock)) < 0) { |
|
223 |
- perror("connect()"); |
|
224 |
- logg("!Can't connect to clamd for streaming.\n"); |
|
225 |
- close(wsockd); |
|
226 |
- close(fd); |
|
227 |
- return -1; |
|
228 |
- } |
|
229 |
- while((len = read(fd, buf, sizeof(buf))) > 0) { |
|
230 |
- if(sendln(wsockd, buf, len)) { /* FIXME: conn might be closed unexpectedly due to limits */ |
|
231 |
- logg("!Can't write to the socket.\n"); |
|
232 |
- close(wsockd); |
|
233 |
- close(fd); |
|
234 |
- return -1; |
|
235 |
- } |
|
236 |
- } |
|
237 |
- close(wsockd); |
|
238 |
- close(fd); |
|
239 |
- if(len) { |
|
240 |
- logg("!Failed to read from %s.\n", filename); |
|
241 |
- return -1; |
|
242 |
- } |
|
243 |
- break; |
|
244 |
- } |
|
204 |
+ if(send_stream(sockd, filename)) |
|
205 |
+ return -1; |
|
206 |
+ |
|
245 | 207 |
#ifdef HAVE_FD_PASSING |
246 | 208 |
case FILDES: |
247 |
- { |
|
248 |
- struct iovec iov[1]; |
|
249 |
- struct msghdr msg; |
|
250 |
- struct cmsghdr *cmsg; |
|
251 |
- unsigned char fdbuf[CMSG_SPACE(sizeof(int))]; |
|
252 |
- char dummy[]=""; |
|
253 |
- |
|
254 |
- if(filename) { |
|
255 |
- if(!(fd = open(filename, O_RDONLY))) { |
|
256 |
- logg("!Open failed on %s.\n", filename); |
|
257 |
- return -1; |
|
258 |
- } |
|
259 |
- } else fd = 0; |
|
260 |
- if(sendln(sockd, "zFILDES", 8)) return -1; |
|
261 |
- |
|
262 |
- iov[0].iov_base = dummy; |
|
263 |
- iov[0].iov_len = 1; |
|
264 |
- memset(&msg, 0, sizeof(msg)); |
|
265 |
- msg.msg_control = fdbuf; |
|
266 |
- msg.msg_iov = iov; |
|
267 |
- msg.msg_iovlen = 1; |
|
268 |
- msg.msg_controllen = CMSG_LEN(sizeof(int)); |
|
269 |
- cmsg = CMSG_FIRSTHDR(&msg); |
|
270 |
- cmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
|
271 |
- cmsg->cmsg_level = SOL_SOCKET; |
|
272 |
- cmsg->cmsg_type = SCM_RIGHTS; |
|
273 |
- *(int *)CMSG_DATA(cmsg) = fd; |
|
274 |
- if(sendmsg(sockd, &msg, 0) == -1) { |
|
275 |
- logg("!FD send failed\n"); |
|
276 |
- return -1; |
|
277 |
- } |
|
278 |
- break; |
|
279 |
- } |
|
209 |
+ if(send_fdpass(sockd, filename)) |
|
210 |
+ return -1; |
|
211 |
+ break; |
|
280 | 212 |
#endif |
281 | 213 |
} |
282 | 214 |
|
... | ... |
@@ -349,6 +344,7 @@ static int isremote(const struct optstruct *opts) { |
349 | 349 |
struct hostent *he; |
350 | 350 |
struct optstruct *clamdopts; |
351 | 351 |
const char *clamd_conf = optget(opts, "config-file")->strarg; |
352 |
+ static struct sockaddr_in testsock; |
|
352 | 353 |
|
353 | 354 |
if((clamdopts = optparse(clamd_conf, 0, NULL, 1, OPT_CLAMD, 0, NULL)) == NULL) { |
354 | 355 |
logg("!Can't parse clamd configuration file %s\n", clamd_conf); |
... | ... |
@@ -361,9 +357,6 @@ static int isremote(const struct optstruct *opts) { |
361 | 361 |
nixsock.sun_path[sizeof(nixsock.sun_path)-1]='\0'; |
362 | 362 |
mainsa = (struct sockaddr *)&nixsock; |
363 | 363 |
mainsasz = sizeof(nixsock); |
364 |
- memset((void *)&strmsock, 0, sizeof(strmsock)); |
|
365 |
- strmsock.sin_family = AF_INET; |
|
366 |
- strmsock.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
|
367 | 364 |
optfree(clamdopts); |
368 | 365 |
return 0; |
369 | 366 |
} |
... | ... |
@@ -374,8 +367,7 @@ static int isremote(const struct optstruct *opts) { |
374 | 374 |
mainsa = (struct sockaddr *)&tcpsock; |
375 | 375 |
mainsasz = sizeof(tcpsock); |
376 | 376 |
memset((void *)&tcpsock, 0, sizeof(tcpsock)); |
377 |
- memset((void *)&strmsock, 0, sizeof(strmsock)); |
|
378 |
- tcpsock.sin_family = strmsock.sin_family = AF_INET; |
|
377 |
+ tcpsock.sin_family = AF_INET; |
|
379 | 378 |
tcpsock.sin_port = htons(opt->numarg); |
380 | 379 |
if(!(opt = optget(clamdopts, "TCPAddr"))->enabled) { |
381 | 380 |
tcpsock.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
... | ... |
@@ -390,23 +382,24 @@ static int isremote(const struct optstruct *opts) { |
390 | 390 |
mainsa = NULL; |
391 | 391 |
return 0; |
392 | 392 |
} |
393 |
- strmsock.sin_port = htons(INADDR_ANY); |
|
394 |
- tcpsock.sin_addr = strmsock.sin_addr = *(struct in_addr *) he->h_addr_list[0]; |
|
395 |
- if(!(s = socket(tcpsock.sin_family, SOCK_STREAM, 0))) return 0; |
|
396 |
- ret = (bind(s, (struct sockaddr *)&strmsock, sizeof(strmsock)) != 0); |
|
393 |
+ tcpsock.sin_addr = *(struct in_addr *) he->h_addr_list[0]; |
|
394 |
+ memcpy((void *)&testsock, (void *)&tcpsock, sizeof(testsock)); |
|
395 |
+ testsock.sin_port = htons(INADDR_ANY); |
|
396 |
+ if(!(s = socket(testsock.sin_family, SOCK_STREAM, 0))) return 0; |
|
397 |
+ ret = (bind(s, (struct sockaddr *)&testsock, sizeof(testsock)) != 0); |
|
397 | 398 |
close(s); |
398 | 399 |
return ret; |
399 | 400 |
} |
400 | 401 |
|
401 |
-struct client_cb_data { |
|
402 |
+struct client_serial_data { |
|
402 | 403 |
int infected; |
403 | 404 |
int errors; |
404 | 405 |
int scantype; |
405 | 406 |
int spam; |
406 | 407 |
}; |
407 | 408 |
|
408 |
-static int callback(struct stat *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) { |
|
409 |
- struct client_cb_data *c = (struct client_cb_data *)data->data; |
|
409 |
+static int serial_callback(struct stat *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) { |
|
410 |
+ struct client_serial_data *c = (struct client_serial_data *)data->data; |
|
410 | 411 |
int sockd, ret; |
411 | 412 |
const char *f = filename; |
412 | 413 |
|
... | ... |
@@ -451,11 +444,32 @@ static int callback(struct stat *sb, char *filename, const char *path, enum cli_ |
451 | 451 |
return CL_SUCCESS; |
452 | 452 |
} |
453 | 453 |
|
454 |
-static int client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel) { |
|
455 |
- struct cli_ftw_cbdata data; |
|
456 |
- struct client_cb_data cdata; |
|
457 |
- char *fullpath; |
|
454 |
+static char *makeabs(const char *basepath) { |
|
458 | 455 |
int namelen; |
456 |
+ char *ret; |
|
457 |
+ |
|
458 |
+ if(!(ret = malloc(PATH_MAX + 1))) { |
|
459 |
+ logg("^Can't make room for fullpath.\n"); |
|
460 |
+ return NULL; |
|
461 |
+ } |
|
462 |
+ if(*basepath != '/') { /* FIXME: to be unified */ |
|
463 |
+ if(!getcwd(ret, PATH_MAX)) { |
|
464 |
+ logg("^Can't get absolute pathname of current working directory.\n"); |
|
465 |
+ free(ret); |
|
466 |
+ return NULL; |
|
467 |
+ } |
|
468 |
+ namelen = strlen(ret); |
|
469 |
+ snprintf(&ret[namelen], PATH_MAX - namelen, "/%s", basepath); |
|
470 |
+ } else { |
|
471 |
+ strncpy(ret, basepath, PATH_MAX); |
|
472 |
+ } |
|
473 |
+ ret[PATH_MAX] = '\0'; |
|
474 |
+ return ret; |
|
475 |
+} |
|
476 |
+ |
|
477 |
+static int serial_client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel) { |
|
478 |
+ struct cli_ftw_cbdata data; |
|
479 |
+ struct client_serial_data cdata; |
|
459 | 480 |
|
460 | 481 |
cdata.infected = 0; |
461 | 482 |
cdata.errors = 0; |
... | ... |
@@ -463,34 +477,134 @@ static int client_scan(const char *file, int scantype, int *infected, int *error |
463 | 463 |
cdata.spam = 0; |
464 | 464 |
data.data = &cdata; |
465 | 465 |
|
466 |
- if(!(fullpath = malloc(PATH_MAX + 1))) { |
|
467 |
- logg("^Can't make room for fullpath.\n"); |
|
468 |
- (*errors)++; |
|
469 |
- return 1; |
|
466 |
+ cli_ftw(file, CLI_FTW_STD, maxlevel ? maxlevel : INT_MAX, serial_callback, &data); |
|
467 |
+ if(!cdata.infected && (!cdata.errors || cdata.spam)) logg("~%s: OK\n", file); |
|
468 |
+ |
|
469 |
+ *infected += cdata.infected; |
|
470 |
+ *errors += cdata.errors; |
|
471 |
+ return 0; |
|
472 |
+} |
|
473 |
+ |
|
474 |
+struct client_parallel_data { |
|
475 |
+ int infected; |
|
476 |
+ int errors; |
|
477 |
+ int scantype; |
|
478 |
+ int spam; |
|
479 |
+ int sockd; |
|
480 |
+ int lastid; |
|
481 |
+ struct SCANID { |
|
482 |
+ unsigned int id; |
|
483 |
+ const char *file; |
|
484 |
+ struct SCANID *next; |
|
485 |
+ } *ids; |
|
486 |
+}; |
|
487 |
+ |
|
488 |
+static int parallel_callback(struct stat *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) { |
|
489 |
+ struct client_parallel_data *c = (struct client_parallel_data *)data->data; |
|
490 |
+ struct SCANID **id = &c->ids, *cid; |
|
491 |
+ fd_set fds; |
|
492 |
+ struct timeval tv = { 0, 0 }; |
|
493 |
+ |
|
494 |
+ switch(reason) { |
|
495 |
+ case error_stat: |
|
496 |
+ logg("^Can't access file %s\n", filename); |
|
497 |
+ return CL_SUCCESS; |
|
498 |
+ case error_mem: |
|
499 |
+ logg("^Memory allocation failed in ftw\n"); |
|
500 |
+ return CL_EMEM; |
|
501 |
+ case warning_skipped_dir: |
|
502 |
+ logg("^Directory recursion limit reached\n"); |
|
503 |
+ return CL_SUCCESS; |
|
504 |
+ case warning_skipped_special: |
|
505 |
+ logg("~%s: Not supported file type. ERROR\n", filename); |
|
506 |
+ c->errors++; |
|
507 |
+ return CL_SUCCESS; |
|
508 |
+ case visit_directory_toplev: |
|
509 |
+ c->spam = 1; |
|
510 |
+ free(filename); |
|
511 |
+ return CL_SUCCESS; |
|
512 |
+ default: |
|
513 |
+ break; |
|
470 | 514 |
} |
471 |
- if(*file != '/') { /* FIXME: to be unified */ |
|
472 |
- if(!getcwd(fullpath, PATH_MAX)) { |
|
473 |
- logg("^Can't get absolute pathname of current working directory.\n"); |
|
474 |
- free(fullpath); |
|
475 |
- (*errors)++; |
|
476 |
- return 1; |
|
477 |
- } |
|
478 |
- namelen = strlen(fullpath); |
|
479 |
- snprintf(&fullpath[namelen], PATH_MAX - namelen, "/%s", file); |
|
480 |
- } else { |
|
481 |
- strncpy(fullpath, file, PATH_MAX); |
|
515 |
+ |
|
516 |
+ while (*id) |
|
517 |
+ id = &((*id)->next); |
|
518 |
+ cid = (struct SCANID *)malloc(sizeof(struct SCANID *)); |
|
519 |
+ *id = cid; |
|
520 |
+ cid->id = ++c->lastid; |
|
521 |
+ cid->file = filename; |
|
522 |
+ cid->next = NULL; |
|
523 |
+ |
|
524 |
+ switch(c->scantype) { |
|
525 |
+ case FILDES: |
|
526 |
+ send_fdpass(c->sockd, filename); /* FIXME: check return */ |
|
527 |
+ break; |
|
528 |
+ case STREAM: |
|
529 |
+ send_stream(c->sockd, filename); /* FIXME: check return */ |
|
530 |
+ break; |
|
482 | 531 |
} |
483 |
- fullpath[PATH_MAX] = '\0'; |
|
532 |
+/* if((ret = dsresult(sockd, c->scantype, f)) >= 0) */ |
|
533 |
+/* c->infected += ret; */ |
|
534 |
+/* else */ |
|
535 |
+/* c->errors++; */ |
|
536 |
+ |
|
537 |
+ FD_ZERO(&fds); |
|
538 |
+ FD_SET(c->sockd, &fds); |
|
539 |
+ switch (select(1, &fds, NULL, NULL, &tv)) { |
|
540 |
+ case -1: |
|
541 |
+ logg("!select failed during session\n"); |
|
542 |
+ return CL_BREAK; /* this is an hard failure */ |
|
543 |
+ case 0: |
|
544 |
+ return CL_SUCCESS; |
|
545 |
+ } |
|
546 |
+ |
|
547 |
+ /* FIXME: recv / parse line here, possibly unify with dsresult */ |
|
548 |
+ return CL_SUCCESS; |
|
549 |
+} |
|
484 | 550 |
|
485 |
- cli_ftw(fullpath, CLI_FTW_STD, maxlevel ? maxlevel : INT_MAX, callback, &data); |
|
486 |
- if(!cdata.infected && (!cdata.errors || cdata.spam)) logg("~%s: OK\n", fullpath); |
|
487 |
- free(fullpath); |
|
551 |
+static int parallel_client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel) { |
|
552 |
+ struct cli_ftw_cbdata data; |
|
553 |
+ struct client_parallel_data cdata; |
|
554 |
+ |
|
555 |
+ if((cdata.sockd = dconnect()) < 0) |
|
556 |
+ return 1; |
|
557 |
+ |
|
558 |
+ if(sendln(cdata.sockd, "zIDSESSION", 11)) { |
|
559 |
+ close(cdata.sockd); |
|
560 |
+ return 1; |
|
561 |
+ } |
|
562 |
+ |
|
563 |
+ cdata.infected = 0; |
|
564 |
+ cdata.errors = 0; |
|
565 |
+ cdata.scantype = scantype; |
|
566 |
+ cdata.spam = 0; |
|
567 |
+ cdata.lastid = 0; |
|
568 |
+ data.data = &cdata; |
|
569 |
+ |
|
570 |
+ cli_ftw(file, CLI_FTW_STD, maxlevel ? maxlevel : INT_MAX, parallel_callback, &data); |
|
571 |
+ if(!cdata.infected && (!cdata.errors || cdata.spam)) logg("~%s: OK\n", file); |
|
488 | 572 |
|
489 | 573 |
*infected += cdata.infected; |
490 | 574 |
*errors += cdata.errors; |
491 | 575 |
return 0; |
492 | 576 |
} |
493 | 577 |
|
578 |
+static int client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel, int session) { |
|
579 |
+ int ret; |
|
580 |
+ char *fullpath = makeabs(file); |
|
581 |
+ |
|
582 |
+ if(!fullpath) { |
|
583 |
+ (*errors)++; |
|
584 |
+ return 1; |
|
585 |
+ } |
|
586 |
+ if (!session) |
|
587 |
+ ret = serial_client_scan(fullpath, scantype, infected, errors, maxlevel); |
|
588 |
+ else |
|
589 |
+ ret = parallel_client_scan(fullpath, scantype, infected, errors, maxlevel); |
|
590 |
+ free(fullpath); |
|
591 |
+ return 0; |
|
592 |
+} |
|
593 |
+ |
|
494 | 594 |
int get_clamd_version(const struct optstruct *opts) |
495 | 595 |
{ |
496 | 596 |
char *buff; |
... | ... |
@@ -596,10 +710,10 @@ int client(const struct optstruct *opts, int *infected) |
596 | 596 |
logg("!Scanning from standard input requires \"-\" to be the only file argument\n"); |
597 | 597 |
continue; |
598 | 598 |
} |
599 |
- if(client_scan(opts->filename[i], scantype, infected, &errors, maxrec)) break; |
|
599 |
+ if(client_scan(opts->filename[i], scantype, infected, &errors, maxrec, session)) break; |
|
600 | 600 |
} |
601 | 601 |
} else { |
602 |
- client_scan("", scantype, infected, &errors, maxrec); |
|
602 |
+ client_scan("", scantype, infected, &errors, maxrec, session); |
|
603 | 603 |
} |
604 | 604 |
return *infected ? 1 : (errors ? 2 : 0); |
605 | 605 |
} |