Browse code

initial idsession implementation

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
Showing 1 changed files
... ...
@@ -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
 }