Browse code

IDSESSION + INSTREAM sport tidy (smthng still fuxxxd up)

git-svn-id: file:///var/lib/svn/clamav-devel/branches/clamd-proto@4693 77e5149b-7576-45b1-b177-96237e5ba77b

aCaB authored on 2009/02/07 09:59:58
Showing 6 changed files
... ...
@@ -31,9 +31,10 @@ clamdscan_SOURCES = \
31 31
     $(top_srcdir)/shared/getopt.h \
32 32
     $(top_srcdir)/libclamav/regex/strlcpy.c\
33 33
     clamdscan.c \
34
+    proto.c \
35
+    proto.h \
34 36
     client.c \
35 37
     client.h
36
-
37 38
 endif
38 39
 
39 40
 DEFS = @DEFS@ -DCL_NOTHREADS -DCL_NOLIBCLAMAV
... ...
@@ -76,12 +76,13 @@ am__clamdscan_SOURCES_DIST = $(top_srcdir)/shared/output.c \
76 76
 	$(top_srcdir)/shared/optparser.h $(top_srcdir)/shared/misc.c \
77 77
 	$(top_srcdir)/shared/misc.h $(top_srcdir)/shared/getopt.c \
78 78
 	$(top_srcdir)/shared/getopt.h \
79
-	$(top_srcdir)/libclamav/regex/strlcpy.c clamdscan.c client.c \
80
-	client.h
79
+	$(top_srcdir)/libclamav/regex/strlcpy.c clamdscan.c proto.c \
80
+	proto.h client.c client.h
81 81
 @BUILD_CLAMD_TRUE@am_clamdscan_OBJECTS = output.$(OBJEXT) \
82 82
 @BUILD_CLAMD_TRUE@	optparser.$(OBJEXT) misc.$(OBJEXT) \
83 83
 @BUILD_CLAMD_TRUE@	getopt.$(OBJEXT) strlcpy.$(OBJEXT) \
84
-@BUILD_CLAMD_TRUE@	clamdscan.$(OBJEXT) client.$(OBJEXT)
84
+@BUILD_CLAMD_TRUE@	clamdscan.$(OBJEXT) proto.$(OBJEXT) \
85
+@BUILD_CLAMD_TRUE@	client.$(OBJEXT)
85 86
 clamdscan_OBJECTS = $(am_clamdscan_OBJECTS)
86 87
 clamdscan_LDADD = $(LDADD)
87 88
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
... ...
@@ -268,6 +269,8 @@ top_srcdir = @top_srcdir@
268 268
 @BUILD_CLAMD_TRUE@    $(top_srcdir)/shared/getopt.h \
269 269
 @BUILD_CLAMD_TRUE@    $(top_srcdir)/libclamav/regex/strlcpy.c\
270 270
 @BUILD_CLAMD_TRUE@    clamdscan.c \
271
+@BUILD_CLAMD_TRUE@    proto.c \
272
+@BUILD_CLAMD_TRUE@    proto.h \
271 273
 @BUILD_CLAMD_TRUE@    client.c \
272 274
 @BUILD_CLAMD_TRUE@    client.h
273 275
 
... ...
@@ -367,6 +370,7 @@ distclean-compile:
367 367
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
368 368
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/optparser.Po@am__quote@
369 369
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
370
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proto.Po@am__quote@
370 371
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlcpy.Po@am__quote@
371 372
 
372 373
 .c.o:
... ...
@@ -1,5 +1,7 @@
1 1
 /*
2
- *  Copyright (C) 2002 - 2007 Tomasz Kojm <tkojm@clamav.net>
2
+ *  Copyright (C) 2009 Sourcefire, Inc.
3
+ *
4
+ *  Authors: Tomasz Kojm, aCaB
3 5
  *
4 6
  *  This program is free software; you can redistribute it and/or modify
5 7
  *  it under the terms of the GNU General Public License version 2 as
... ...
@@ -51,28 +53,25 @@
51 51
 #include "libclamav/others.h"
52 52
 
53 53
 #include "client.h"
54
+#include "proto.h"
54 55
 
55 56
 #ifndef INADDR_LOOPBACK
56 57
 #define INADDR_LOOPBACK 0x7f000001
57 58
 #endif
58 59
 
60
+#ifndef PATH_MAX
61
+#define PATH_MAX 1024
62
+#endif
63
+
59 64
 int notremoved = 0, notmoved = 0;
60 65
 int printinfected = 0;
61 66
 
62
-static struct sockaddr *mainsa = NULL;
63
-static int mainsasz;
67
+struct sockaddr *mainsa = NULL;
68
+int mainsasz;
64 69
 static struct sockaddr_un nixsock;
65 70
 static struct sockaddr_in tcpsock;
66
-enum {
67
-    CONT,
68
-    MULTI,
69
-    STREAM,
70
-    FILDES
71
-};
72
-
73
-static const char *scancmd[] = { "CONTSCAN", "MULTISCAN" };
74 71
 
75
-static void (*action)(const char *) = NULL;
72
+void (*action)(const char *) = NULL;
76 73
 static char *actarget;
77 74
 static void move_infected(const char *filename, int move);
78 75
 static void action_move(const char *filename) {
... ...
@@ -102,241 +101,6 @@ void actsetup(const struct optstruct *opts) {
102 102
     }
103 103
 }
104 104
 
105
-static int sendln(int sockd, const char *line, unsigned int len) {
106
-    while(len) {
107
-	int sent = send(sockd, line, len, 0);
108
-	if(sent <= 0) {
109
-	    if(sent && errno == EINTR) continue;
110
-	    logg("!Can't send request to clamd\n");
111
-	    return 1;
112
-	}
113
-	line += sent;
114
-	len -= sent;
115
-    }
116
-    return 0;
117
-}
118
-
119
-struct RCVLN {
120
-    char buf[PATH_MAX+1024]; /* FIXME must match that in clamd - bb1349 */
121
-    int sockd;
122
-    int r;
123
-    char *cur;
124
-    char *bol;
125
-};
126
-
127
-static void recvlninit(struct RCVLN *s, int sockd) {
128
-    s->sockd = sockd;
129
-    s->bol = s->cur = s->buf;
130
-    s->r = 0;
131
-}
132
-
133
-static int recvln(struct RCVLN *s, char **rbol, char **reol) {
134
-    char *eol;
135
-
136
-    while(1) {
137
-	if(!s->r) {
138
-	    s->r = recv(s->sockd, s->cur, sizeof(s->buf) - (s->cur - s->buf), 0);
139
-	    if(s->r<=0) {
140
-		if(s->r && errno == EINTR) {
141
-		    s->r = 0;
142
-		    continue;
143
-		}
144
-		if(s->r || s->cur!=s->buf) {
145
-		    logg("!Communication error\n");
146
-		    return -1;
147
-		}
148
-	        return 0;
149
-	    }
150
-	}
151
-	if((eol = memchr(s->cur, 0, s->r))) {
152
-	    int ret = 0;
153
-	    eol++;
154
-	    s->r -= eol - s->cur;
155
-	    *rbol = s->bol;
156
-	    if(reol) *reol = eol;
157
-	    ret = eol - s->bol;
158
-	    if(s->r)
159
-		s->bol = s->cur = eol;
160
-	    else
161
-		s->bol = s->cur = s->buf;
162
-	    return ret;
163
-	}
164
-	s->r += s->cur - s->bol;
165
-	if(!eol && s->r==sizeof(s->buf)) {
166
-	    logg("!Overlong reply from clamd\n");
167
-	    return -1;
168
-	}
169
-	if(!eol) {
170
-	    if(s->buf != s->bol) { /* old memmove sux */
171
-		memmove(s->buf, s->bol, s->r);
172
-		s->bol = s->buf;
173
-	    }
174
-	    s->cur = &s->bol[s->r];
175
-	    s->r = 0;
176
-	}
177
-    }
178
-}
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: need to handle 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
-
244
-static int dsresult(int sockd, int scantype, const char *filename)
245
-{
246
-	int infected = 0, waserror = 0, len;
247
-	char *bol, *eol;
248
-	struct RCVLN rcv;
249
-
250
-    recvlninit(&rcv, sockd);
251
-
252
-    switch(scantype) {
253
-    case MULTI:
254
-    case CONT:
255
-    len = strlen(filename) + strlen(scancmd[scantype]) + 3;
256
-    if (!(bol = malloc(len))) {
257
-	logg("!Cannot allocate a command buffer\n");
258
-	return -1;
259
-    }
260
-    sprintf(bol, "z%s %s", scancmd[scantype], filename);
261
-    if(sendln(sockd, bol, len)) return -1;
262
-    free(bol);
263
-    break;
264
-
265
-    case STREAM:
266
-	if(send_stream(sockd, filename))
267
-	    return -1;
268
-
269
-#ifdef HAVE_FD_PASSING
270
-    case FILDES:
271
-	if(send_fdpass(sockd, filename))
272
-	    return -1;
273
-	break;
274
-#endif
275
-    }
276
-
277
-    while((len = recvln(&rcv, &bol, &eol))) {
278
-	if(len == -1) {
279
-	    waserror = 1;
280
-	    break;
281
-	}
282
-	if(!filename) logg("~%s\n", bol);
283
-	if(len > 7) {
284
-	    char *colon = colon = strrchr(bol, ':');
285
-	    if(!colon) {
286
-		logg("Failed to parse reply\n");
287
-		waserror = 1;
288
-	    } else if(!memcmp(eol - 7, " FOUND", 6)) {
289
-		infected++;
290
-		if(filename) {
291
-		    if(scantype >= STREAM) {
292
-			logg("~%s%s\n", filename, colon);
293
-			if(action) action(filename);
294
-		    } else {
295
-			logg("~%s\n", bol);
296
-			*colon = '\0';
297
-			if(action)
298
-			    action(bol);
299
-		    }
300
-		}
301
-	    } else if(!memcmp(eol-7, " ERROR", 6)) {
302
-		if(filename) {
303
-		    if(scantype >= STREAM)
304
-			logg("~%s%s\n", filename, colon);
305
-		    else
306
-			logg("~%s\n", bol);
307
-		}
308
-		waserror = 1;
309
-	    }
310
-	}
311
-    }
312
-    return infected ? infected : (waserror ? -1 : 0);
313
-}
314
-
315
-
316
-#ifndef PATH_MAX
317
-#define PATH_MAX 1024
318
-#endif
319
-
320
-static int dconnect()
321
-{
322
-	int sockd;
323
-
324
-    if((sockd = socket(mainsa->sa_family, SOCK_STREAM, 0)) < 0) {
325
-	perror("socket()");
326
-	logg("!Can't create the socket.\n");
327
-	return -1;
328
-    }
329
-
330
-    if(connect(sockd, (struct sockaddr *)mainsa, mainsasz) < 0) {
331
-	close(sockd);
332
-	perror("connect()");
333
-	logg("!Can't connect to clamd.\n");
334
-	return -1;
335
-    }
336
-
337
-    return sockd;
338
-}
339
-
340 105
 static int isremote(const struct optstruct *opts) {
341 106
     int s, ret;
342 107
     const struct optstruct *opt;
... ...
@@ -390,58 +154,6 @@ static int isremote(const struct optstruct *opts) {
390 390
     return ret;
391 391
 }
392 392
 
393
-struct client_serial_data {
394
-    int infected;
395
-    int errors;
396
-    int scantype;
397
-    int spam;
398
-};
399
-
400
-static int serial_callback(struct stat *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) {
401
-    struct client_serial_data *c = (struct client_serial_data *)data->data;
402
-    int sockd, ret;
403
-    const char *f = filename;
404
-
405
-    switch(reason) {
406
-    case error_stat:
407
-	logg("^Can't access file %s\n", filename);
408
-	return CL_SUCCESS;
409
-    case error_mem:
410
-	logg("^Memory allocation failed in ftw\n");
411
-	return CL_EMEM;
412
-    case warning_skipped_dir:
413
-	logg("^Directory recursion limit reached\n");
414
-	return CL_SUCCESS;
415
-    case warning_skipped_special:
416
-	logg("~%s: Not supported file type. ERROR\n", filename);
417
-	c->errors++;
418
-	return CL_SUCCESS;
419
-    default:
420
-	break;
421
-    }
422
-
423
-    if(reason == visit_directory_toplev) {
424
-	c->spam = 1;
425
-	if(c->scantype >= STREAM) {
426
-	    free(filename);
427
-	    return CL_SUCCESS;
428
-	}
429
-	f = path;
430
-    }
431
-    if((sockd = dconnect()) < 0) {
432
-	free(filename);
433
-	return CL_BREAK;
434
-    }
435
-    if((ret = dsresult(sockd, c->scantype, f)) >= 0)
436
-	c->infected += ret;
437
-    else
438
-	c->errors++;
439
-    close(sockd);
440
-    free(filename);
441
-    if(reason == visit_directory_toplev)
442
-	return CL_BREAK;
443
-    return CL_SUCCESS;
444
-}
445 393
 
446 394
 static char *makeabs(const char *basepath) {
447 395
     int namelen;
... ...
@@ -466,173 +178,7 @@ static char *makeabs(const char *basepath) {
466 466
     return ret;
467 467
 }
468 468
 
469
-static int serial_client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel) {
470
-    struct cli_ftw_cbdata data;
471
-    struct client_serial_data cdata;
472
-
473
-    cdata.infected = 0;
474
-    cdata.errors = 0;
475
-    cdata.scantype = scantype;
476
-    cdata.spam = 0;
477
-    data.data = &cdata;
478
-
479
-    cli_ftw(file, CLI_FTW_STD, maxlevel ? maxlevel : INT_MAX, serial_callback, &data);
480
-    if(!cdata.infected && (!cdata.errors || cdata.spam)) logg("~%s: OK\n", file);
481
-
482
-    *infected += cdata.infected;
483
-    *errors += cdata.errors;
484
-    return 0;
485
-}
486
-
487
-struct client_parallel_data {
488
-    int infected;
489
-    int errors;
490
-    int scantype;
491
-    int spam;
492
-    int sockd;
493
-    int lastid;
494
-    struct SCANID {
495
-	unsigned int id;
496
-	const char *file;
497
-	struct SCANID *next;
498
-    } *ids;
499
-};
500
-
501
-int dspresult(struct client_parallel_data *c) {
502
-    int rid, len;
503
-    char *bol, *eol, *filename;
504
-    fd_set fds;
505
-    struct SCANID **id = NULL;
506
-    struct timeval tv = { 0, 0 };
507
-    struct RCVLN rcv;
508
-
509
-    recvlninit(&rcv, c->sockd);
510
-    while(1) {
511
-	FD_ZERO(&fds);
512
-	FD_SET(c->sockd, &fds);
513
-	switch (select(1, &fds, NULL, NULL, &tv)) {
514
-	case -1:
515
-	    c->errors++;
516
-	    logg("!select failed during session\n");
517
-	    return CL_BREAK; /* this is an hard failure */
518
-	case 0:
519
-	    c->errors++;
520
-	    return CL_SUCCESS;
521
-	}
522
-
523
-	/* FIXME: repeat till i have stuff in the buff */
524
-	len = recvln(&rcv, &bol, &eol);
525
-	if(len == -1) {
526
-	    c->errors++;
527
-	    break;
528
-	}
529
-	if((rid = atoi(bol))) {
530
-	    id = &c->ids;
531
-	    while(*id) {
532
-		if((*id)->id == rid) break;
533
-		id = &((*id)->next);
534
-	    }
535
-	    if(!*id) id = NULL;
536
-	}
537
-	if(!id) {
538
-	    c->errors++;
539
-	    logg("!Bogus session id from clamd\n");
540
-	    return CL_BREAK; /* this is an hard failure */
541
-	}
542
-	filename = (*id)->file;
543
-	if(len > 7) {
544
-	    char *colon = colon = strrchr(bol, ':');
545
-	    if(!colon) {
546
-		c->errors++;
547
-		logg("Failed to parse reply\n");
548
-	    } else if(!memcmp(eol - 7, " FOUND", 6)) {
549
-		c->infected++;
550
-		logg("~%s%s\n", filename, colon);
551
-		if(action) action(filename);
552
-	    } else if(!memcmp(eol-7, " ERROR", 6)) {
553
-		c->errors++;
554
-		if(filename)
555
-		    logg("~%s%s\n", filename, colon);
556
-	    }
557
-	}
558
-    }
559
-    return CL_SUCCESS;
560
-}
561
-
562
-
563
-static int parallel_callback(struct stat *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) {
564
-    struct client_parallel_data *c = (struct client_parallel_data *)data->data;
565
-    struct SCANID **id = &c->ids, *cid;
566
-
567
-    switch(reason) {
568
-    case error_stat:
569
-	logg("^Can't access file %s\n", filename);
570
-	return CL_SUCCESS;
571
-    case error_mem:
572
-	logg("^Memory allocation failed in ftw\n");
573
-	return CL_EMEM;
574
-    case warning_skipped_dir:
575
-	logg("^Directory recursion limit reached\n");
576
-	return CL_SUCCESS;
577
-    case warning_skipped_special:
578
-	logg("~%s: Not supported file type. ERROR\n", filename);
579
-	c->errors++;
580
-	return CL_SUCCESS;
581
-    case visit_directory_toplev:
582
-	c->spam = 1;
583
-	free(filename);
584
-	return CL_SUCCESS;
585
-    default:
586
-	break;
587
-    }
588 469
 
589
-    while (*id)
590
-	id = &((*id)->next);
591
-    cid = (struct SCANID *)malloc(sizeof(struct SCANID *));
592
-    *id = cid;
593
-    cid->id = ++c->lastid;
594
-    cid->file = filename;
595
-    cid->next = NULL;
596
-
597
-    switch(c->scantype) {
598
-    case FILDES:
599
-	send_fdpass(c->sockd, filename); /* FIXME: check return */
600
-	break;
601
-    case STREAM:
602
-	send_stream(c->sockd, filename); /* FIXME: check return */
603
-	break;
604
-    }
605
-
606
-    return dspresult(c);
607
-}
608
-
609
-static int parallel_client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel) {
610
-    struct cli_ftw_cbdata data;
611
-    struct client_parallel_data cdata;
612
-
613
-    if((cdata.sockd = dconnect()) < 0)
614
-	return 1;
615
-
616
-    if(sendln(cdata.sockd, "zIDSESSION", 11)) {
617
-	close(cdata.sockd);
618
-	return 1;
619
-    }
620
-
621
-    cdata.infected = 0;
622
-    cdata.errors = 0;
623
-    cdata.scantype = scantype;
624
-    cdata.spam = 0;
625
-    cdata.lastid = 0;
626
-    data.data = &cdata;
627
-
628
-    cli_ftw(file, CLI_FTW_STD, maxlevel ? maxlevel : INT_MAX, parallel_callback, &data);
629
-    /* FIXME: check return */
630
-    if(!cdata.infected && (!cdata.errors || cdata.spam)) logg("~%s: OK\n", file);
631
-
632
-    *infected += cdata.infected;
633
-    *errors += cdata.errors;
634
-    return 0;
635
-}
636 470
 
637 471
 static int client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel, int session) {
638 472
     int ret;
... ...
@@ -1,5 +1,7 @@
1 1
 /*
2
- *  Copyright (C) 2002 Tomasz Kojm <tkojm@clamav.net>
2
+ *  Copyright (C) 2009 Sourcefire, Inc.
3
+ *
4
+ *  Authors: Tomasz Kojm, aCaB
3 5
  *
4 6
  *  This program is free software; you can redistribute it and/or modify
5 7
  *  it under the terms of the GNU General Public License version 2 as
... ...
@@ -21,6 +23,13 @@
21 21
 
22 22
 #include "shared/optparser.h"
23 23
 
24
+enum {
25
+    CONT,
26
+    MULTI,
27
+    STREAM,
28
+    FILDES
29
+};
30
+
24 31
 void actsetup(const struct optstruct *opts);
25 32
 int client(const struct optstruct *opts, int *infected);
26 33
 int get_clamd_version(const struct optstruct *opts);
27 34
new file mode 100644
... ...
@@ -0,0 +1,536 @@
0
+/*
1
+ *  Copyright (C) 2009 Sourcefire, Inc.
2
+ *
3
+ *  Authors: Tomasz Kojm, aCaB
4
+ *
5
+ *  This program is free software; you can redistribute it and/or modify
6
+ *  it under the terms of the GNU General Public License version 2 as
7
+ *  published by the Free Software Foundation.
8
+ *
9
+ *  This program is distributed in the hope that it will be useful,
10
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ *  GNU General Public License for more details.
13
+ *
14
+ *  You should have received a copy of the GNU General Public License
15
+ *  along with this program; if not, write to the Free Software
16
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17
+ *  MA 02110-1301, USA.
18
+ */
19
+
20
+#include <stdio.h>
21
+#include <unistd.h>
22
+#include <string.h>
23
+#include <errno.h>
24
+#include <sys/types.h>
25
+#include <sys/stat.h>
26
+#include <fcntl.h>
27
+#include <sys/types.h>
28
+#include <sys/socket.h>
29
+#include <arpa/inet.h>
30
+
31
+#include "libclamav/others.h"
32
+#include "shared/output.h"
33
+
34
+#include "proto.h"
35
+#include "client.h"
36
+
37
+extern struct sockaddr *mainsa;
38
+extern int mainsasz;
39
+extern void (*action)(const char *);
40
+
41
+static const char *scancmd[] = { "CONTSCAN", "MULTISCAN" };
42
+
43
+/* Connects to clamd 
44
+ * Returns a FD or -1 on error */
45
+int dconnect() {
46
+    int sockd;
47
+
48
+    if((sockd = socket(mainsa->sa_family, SOCK_STREAM, 0)) < 0) {
49
+	perror("socket()");
50
+	logg("!Can't create the socket.\n");
51
+	return -1;
52
+    }
53
+
54
+    if(connect(sockd, (struct sockaddr *)mainsa, mainsasz) < 0) {
55
+	close(sockd);
56
+	perror("connect()");
57
+	logg("!Can't connect to clamd.\n");
58
+	return -1;
59
+    }
60
+    return sockd;
61
+}
62
+
63
+/* Sends bytes over a socket
64
+ * Returns 0 on success */
65
+int sendln(int sockd, const char *line, unsigned int len) {
66
+    while(len) {
67
+	int sent = send(sockd, line, len, 0);
68
+	if(sent <= 0) {
69
+	    if(sent && errno == EINTR) continue;
70
+	    logg("!Can't send request to clamd\n");
71
+	    return 1;
72
+	}
73
+	line += sent;
74
+	len -= sent;
75
+    }
76
+    return 0;
77
+}
78
+
79
+/* Inits a RECVLN struct before it can be used in recvln() - see below */
80
+void recvlninit(struct RCVLN *s, int sockd) {
81
+    s->sockd = sockd;
82
+    s->bol = s->cur = s->buf;
83
+    s->r = 0;
84
+}
85
+
86
+/* Receives a full (terminated with \0) line from a socket
87
+ * Sets rbol to the begin of the received line, and optionally 
88
+ * reol to the ond of line.
89
+ * Should be called repeatedly untill all input is conumed
90
+ * Returns 
91
+ * - the lenght of the line (a positive number) on success
92
+ * - 0 if the connection is closed
93
+ * - -1 on error
94
+ */
95
+int recvln(struct RCVLN *s, char **rbol, char **reol) {
96
+    char *eol;
97
+
98
+    while(1) {
99
+	if(!s->r) {
100
+	    s->r = recv(s->sockd, s->cur, sizeof(s->buf) - (s->cur - s->buf), 0);
101
+	    if(s->r<=0) {
102
+		if(s->r && errno == EINTR) {
103
+		    s->r = 0;
104
+		    continue;
105
+		}
106
+		if(s->r || s->cur!=s->buf) {
107
+		    logg("!Communication error\n");
108
+		    return -1;
109
+		}
110
+	        return 0;
111
+	    }
112
+	}
113
+	if((eol = memchr(s->cur, 0, s->r))) {
114
+	    int ret = 0;
115
+	    eol++;
116
+	    s->r -= eol - s->cur;
117
+	    *rbol = s->bol;
118
+	    if(reol) *reol = eol;
119
+	    ret = eol - s->bol;
120
+	    if(s->r)
121
+		s->bol = s->cur = eol;
122
+	    else
123
+		s->bol = s->cur = s->buf;
124
+	    return ret;
125
+	}
126
+	s->r += s->cur - s->bol;
127
+	if(!eol && s->r==sizeof(s->buf)) {
128
+	    logg("!Overlong reply from clamd\n");
129
+	    return -1;
130
+	}
131
+	if(!eol) {
132
+	    if(s->buf != s->bol) { /* old memmove sux */
133
+		memmove(s->buf, s->bol, s->r);
134
+		s->bol = s->buf;
135
+	    }
136
+	    s->cur = &s->bol[s->r];
137
+	    s->r = 0;
138
+	}
139
+    }
140
+}
141
+
142
+/* Issues an INSTREAM command to clamd and streams the given file
143
+ * Returns 0 on success */
144
+static int send_stream(int sockd, const char *filename) {
145
+    uint32_t buf[BUFSIZ/sizeof(uint32_t)];
146
+    int fd, len;
147
+
148
+    if(filename) {
149
+	if(!(fd = open(filename, O_RDONLY))) {
150
+	    logg("!Open failed on %s.\n", filename);
151
+	    return 1;
152
+	}
153
+    } else fd = 0;
154
+
155
+    if(sendln(sockd, "zINSTREAM", 10)) return 1;
156
+
157
+    while((len = read(fd, &buf[1], sizeof(buf) - sizeof(uint32_t))) > 0) {
158
+	buf[0] = htonl(len);
159
+	if(sendln(sockd, (const char *)buf, len+sizeof(uint32_t))) { /* FIXME: need to handle limits */
160
+	    logg("!Can't write to the socket.\n");
161
+	    close(fd);
162
+	    return 1;
163
+	}
164
+    }
165
+    close(fd);
166
+    if(len) {
167
+	logg("!Failed to read from %s.\n", filename);
168
+	return 1;
169
+    }
170
+    return 0;
171
+}
172
+
173
+/* Issues a FILDES command and pass a FD to clamd
174
+ * Returns 0 on success */
175
+static int send_fdpass(int sockd, const char *filename) {
176
+    struct iovec iov[1];
177
+    struct msghdr msg;
178
+    struct cmsghdr *cmsg;
179
+    unsigned char fdbuf[CMSG_SPACE(sizeof(int))];
180
+    char dummy[]="";
181
+    int fd;
182
+
183
+    if(filename) {
184
+	if(!(fd = open(filename, O_RDONLY))) {
185
+	    logg("!Open failed on %s.\n", filename);
186
+	    return 1;
187
+	}
188
+    } else fd = 0;
189
+    if(sendln(sockd, "zFILDES", 8)) return 1;
190
+
191
+    iov[0].iov_base = dummy;
192
+    iov[0].iov_len = 1;
193
+    memset(&msg, 0, sizeof(msg));
194
+    msg.msg_control = fdbuf;
195
+    msg.msg_iov = iov;
196
+    msg.msg_iovlen = 1;
197
+    msg.msg_controllen = CMSG_LEN(sizeof(int));
198
+    cmsg = CMSG_FIRSTHDR(&msg);
199
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
200
+    cmsg->cmsg_level = SOL_SOCKET;
201
+    cmsg->cmsg_type = SCM_RIGHTS;
202
+    *(int *)CMSG_DATA(cmsg) = fd;
203
+    if(sendmsg(sockd, &msg, 0) == -1) {
204
+	logg("!FD send failed\n");
205
+	return 1;
206
+    }
207
+    return 0;
208
+}
209
+
210
+/* Sends a proper scan request to clamd and parses its replies
211
+ * This is used only in non IDSESSION mode
212
+ * Returns the number of infected files or -1 on error */
213
+int dsresult(int sockd, int scantype, const char *filename) {
214
+	int infected = 0, waserror = 0, len;
215
+	char *bol, *eol;
216
+	struct RCVLN rcv;
217
+
218
+    recvlninit(&rcv, sockd);
219
+
220
+    switch(scantype) {
221
+    case MULTI:
222
+    case CONT:
223
+    len = strlen(filename) + strlen(scancmd[scantype]) + 3;
224
+    if (!(bol = malloc(len))) {
225
+	logg("!Cannot allocate a command buffer\n");
226
+	return -1;
227
+    }
228
+    sprintf(bol, "z%s %s", scancmd[scantype], filename);
229
+    if(sendln(sockd, bol, len)) return -1;
230
+    free(bol);
231
+    break;
232
+
233
+    case STREAM:
234
+	if(send_stream(sockd, filename))
235
+	    return -1;
236
+
237
+#ifdef HAVE_FD_PASSING
238
+    case FILDES:
239
+	if(send_fdpass(sockd, filename))
240
+	    return -1;
241
+	break;
242
+#endif
243
+    }
244
+
245
+    while((len = recvln(&rcv, &bol, &eol))) {
246
+	if(len == -1) {
247
+	    waserror = 1;
248
+	    break;
249
+	}
250
+	printf(">%s<\n", bol);
251
+	if(!filename) logg("~%s\n", bol);
252
+	if(len > 7) {
253
+	    char *colon = colon = strrchr(bol, ':');
254
+	    if(!colon) {
255
+		logg("Failed to parse reply\n");
256
+		waserror = 1;
257
+	    } else if(!memcmp(eol - 7, " FOUND", 6)) {
258
+		infected++;
259
+		if(filename) {
260
+		    if(scantype >= STREAM) {
261
+			logg("~%s%s\n", filename, colon);
262
+			if(action) action(filename);
263
+		    } else {
264
+			logg("~%s\n", bol);
265
+			*colon = '\0';
266
+			if(action)
267
+			    action(bol);
268
+		    }
269
+		}
270
+	    } else if(!memcmp(eol-7, " ERROR", 6)) {
271
+		if(filename) {
272
+		    if(scantype >= STREAM)
273
+			logg("~%s%s\n", filename, colon);
274
+		    else
275
+			logg("~%s\n", bol);
276
+		}
277
+		waserror = 1;
278
+	    }
279
+	}
280
+    }
281
+    return infected ? infected : (waserror ? -1 : 0);
282
+}
283
+
284
+
285
+
286
+/* Used by serial_callback() */
287
+struct client_serial_data {
288
+    int infected;
289
+    int errors;
290
+    int scantype;
291
+    int spam;
292
+};
293
+
294
+/* FTW callback for scanning in non IDSESSION mode */
295
+static int serial_callback(struct stat *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) {
296
+    struct client_serial_data *c = (struct client_serial_data *)data->data;
297
+    int sockd, ret;
298
+    const char *f = filename;
299
+
300
+    switch(reason) {
301
+    case error_stat:
302
+	logg("^Can't access file %s\n", filename);
303
+	return CL_SUCCESS;
304
+    case error_mem:
305
+	logg("^Memory allocation failed in ftw\n");
306
+	return CL_EMEM;
307
+    case warning_skipped_dir:
308
+	logg("^Directory recursion limit reached\n");
309
+	return CL_SUCCESS;
310
+    case warning_skipped_special:
311
+	logg("~%s: Not supported file type. ERROR\n", filename);
312
+	c->errors++;
313
+	return CL_SUCCESS;
314
+    default:
315
+	break;
316
+    }
317
+
318
+    if(reason == visit_directory_toplev) {
319
+	c->spam = 1;
320
+	if(c->scantype >= STREAM) {
321
+	    free(filename);
322
+	    return CL_SUCCESS;
323
+	}
324
+	f = path;
325
+    }
326
+    if((sockd = dconnect()) < 0) {
327
+	free(filename);
328
+	return CL_BREAK;
329
+    }
330
+    if((ret = dsresult(sockd, c->scantype, f)) >= 0)
331
+	c->infected += ret;
332
+    else
333
+	c->errors++;
334
+    close(sockd);
335
+    free(filename);
336
+    if(reason == visit_directory_toplev)
337
+	return CL_BREAK;
338
+    return CL_SUCCESS;
339
+}
340
+
341
+/* Non-IDSESSION handler
342
+ * FIXME: returns what ? */
343
+int serial_client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel) {
344
+    struct cli_ftw_cbdata data;
345
+    struct client_serial_data cdata;
346
+
347
+    cdata.infected = 0;
348
+    cdata.errors = 0;
349
+    cdata.scantype = scantype;
350
+    cdata.spam = 0;
351
+    data.data = &cdata;
352
+
353
+    cli_ftw(file, CLI_FTW_STD, maxlevel ? maxlevel : INT_MAX, serial_callback, &data);
354
+    /* FIXME: care about return ? */
355
+    if(!cdata.infected && (!cdata.errors || cdata.spam)) logg("~%s: OK\n", file);
356
+
357
+    *infected += cdata.infected;
358
+    *errors += cdata.errors;
359
+    return 0;
360
+}
361
+
362
+/* Used in IDSESSION mode */
363
+struct client_parallel_data {
364
+    int infected;
365
+    int errors;
366
+    int scantype;
367
+    int spam;
368
+    int sockd;
369
+    int lastid;
370
+    struct SCANID {
371
+	unsigned int id;
372
+	const char *file;
373
+	struct SCANID *next;
374
+    } *ids;
375
+};
376
+
377
+/* Sends a proper scan request to clamd and parses its replies
378
+ * This is used only in IDSESSION mode
379
+ * Returns 0 on success, 1 on hard failures */
380
+int dspresult(struct client_parallel_data *c) {
381
+    const char *filename;
382
+    char *bol, *eol;
383
+    unsigned int rid;
384
+    int len;
385
+    struct SCANID **id = NULL;
386
+    struct RCVLN rcv;
387
+
388
+    recvlninit(&rcv, c->sockd);
389
+    do {
390
+	len = recvln(&rcv, &bol, &eol);
391
+	if(len == -1) {
392
+	    c->errors++;
393
+	    break;
394
+	}
395
+	if((rid = atoi(bol))) {
396
+	    id = &c->ids;
397
+	    while(*id) {
398
+		if((*id)->id == rid) break;
399
+		id = &((*id)->next);
400
+	    }
401
+	    if(!*id) id = NULL;
402
+	}
403
+	if(!id) {
404
+	    c->errors++;
405
+	    logg("!Bogus session id from clamd\n");
406
+	    return 1; /* this is an hard failure */
407
+	}
408
+	filename = (*id)->file;
409
+	if(len > 7) {
410
+	    char *colon = colon = strrchr(bol, ':');
411
+	    if(!colon) {
412
+		c->errors++;
413
+		logg("Failed to parse reply\n");
414
+	    } else if(!memcmp(eol - 7, " FOUND", 6)) {
415
+		c->infected++;
416
+		logg("~%s%s\n", filename, colon);
417
+		if(action) action(filename);
418
+	    } else if(!memcmp(eol-7, " ERROR", 6)) {
419
+		c->errors++;
420
+		if(filename)
421
+		    logg("~%s%s\n", filename, colon);
422
+	    }
423
+	}
424
+	free((void *)filename);
425
+	bol = (char *)*id;
426
+	*id = (*id)->next;
427
+	free(bol);
428
+    } while(rcv.cur != rcv.buf); /* clamd sends whole lines, so, on partial lines, we just assume
429
+				    more data can be recv()'d with close to zero latency */
430
+    return 0;
431
+}
432
+
433
+/* FTW callback for scanning in IDSESSION mode */
434
+static int parallel_callback(struct stat *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) {
435
+    struct client_parallel_data *c = (struct client_parallel_data *)data->data;
436
+    struct SCANID **id = &c->ids, *cid;
437
+
438
+    switch(reason) {
439
+    case error_stat:
440
+	logg("^Can't access file %s\n", filename);
441
+	return CL_SUCCESS;
442
+    case error_mem:
443
+	logg("^Memory allocation failed in ftw\n");
444
+	return CL_EMEM;
445
+    case warning_skipped_dir:
446
+	logg("^Directory recursion limit reached\n");
447
+	return CL_SUCCESS;
448
+    case warning_skipped_special:
449
+	logg("~%s: Not supported file type. ERROR\n", filename);
450
+	c->errors++;
451
+	return CL_SUCCESS;
452
+    case visit_directory_toplev:
453
+	c->spam = 1;
454
+	free(filename);
455
+	return CL_SUCCESS;
456
+    default:
457
+	break;
458
+    }
459
+
460
+    while(1) {
461
+	/* consume all the available input to let some of the clamd
462
+	 * threads blocked on send() to be dead.
463
+	 * by doing so we shouldn't deadlock on the next recv() */
464
+	fd_set rfds, wfds;
465
+	FD_ZERO(&rfds);
466
+	FD_SET(c->sockd, &rfds);
467
+	FD_ZERO(&wfds);
468
+	FD_SET(c->sockd, &wfds);
469
+	if(select(c->sockd + 1, &rfds, &wfds, NULL, NULL) < 0) {
470
+	    if(errno == EINTR) continue;
471
+	    c->errors++;
472
+	    free(filename);
473
+	    logg("!select failed during session\n");
474
+	    return CL_BREAK; /* this is an hard failure */
475
+	}
476
+	if(FD_ISSET(c->sockd, &rfds)) {
477
+	    if(dspresult(c)) {
478
+		free(filename);
479
+		return CL_BREAK;
480
+	    } else continue;
481
+	}
482
+	if(FD_ISSET(c->sockd, &wfds)) break;
483
+    }
484
+
485
+    while (*id)
486
+	id = &((*id)->next);
487
+    cid = (struct SCANID *)malloc(sizeof(struct SCANID *));
488
+    *id = cid;
489
+    cid->id = ++c->lastid;
490
+    cid->file = filename;
491
+    cid->next = NULL;
492
+
493
+    switch(c->scantype) {
494
+    case FILDES:
495
+	send_fdpass(c->sockd, filename); /* FIXME: check return */
496
+	break;
497
+    case STREAM:
498
+	send_stream(c->sockd, filename); /* FIXME: check return */
499
+	break;
500
+    }
501
+
502
+    return CL_SUCCESS;
503
+}
504
+
505
+/* Non-IDSESSION handler
506
+ * FIXME: returns what ? */
507
+int parallel_client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel) {
508
+    struct cli_ftw_cbdata data;
509
+    struct client_parallel_data cdata;
510
+
511
+    if((cdata.sockd = dconnect()) < 0)
512
+	return 1;
513
+
514
+    if(sendln(cdata.sockd, "zIDSESSION", 11)) {
515
+	close(cdata.sockd);
516
+	return 1;
517
+    }
518
+
519
+    cdata.infected = 0;
520
+    cdata.errors = 0;
521
+    cdata.scantype = scantype;
522
+    cdata.spam = 0;
523
+    cdata.lastid = 0;
524
+    data.data = &cdata;
525
+
526
+    cli_ftw(file, CLI_FTW_STD, maxlevel ? maxlevel : INT_MAX, parallel_callback, &data);
527
+    /* FIXME: check return */
528
+    if(!cdata.infected && (!cdata.errors || cdata.spam)) logg("~%s: OK\n", file);
529
+
530
+    /* FIXME: call dspresult in a loop here */
531
+
532
+    *infected += cdata.infected;
533
+    *errors += cdata.errors;
534
+    return 0;
535
+}
0 536
new file mode 100644
... ...
@@ -0,0 +1,38 @@
0
+/*
1
+ *  Copyright (C) 2009 Sourcefire, Inc.
2
+ *
3
+ *  Authors: Tomasz Kojm, aCaB
4
+ *
5
+ *  This program is free software; you can redistribute it and/or modify
6
+ *  it under the terms of the GNU General Public License version 2 as
7
+ *  published by the Free Software Foundation.
8
+ *
9
+ *  This program is distributed in the hope that it will be useful,
10
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ *  GNU General Public License for more details.
13
+ *
14
+ *  You should have received a copy of the GNU General Public License
15
+ *  along with this program; if not, write to the Free Software
16
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17
+ *  MA 02110-1301, USA.
18
+ */
19
+
20
+#ifndef PROTO_H
21
+#define PROTO_H
22
+struct RCVLN {
23
+    char buf[PATH_MAX+1024]; /* FIXME must match that in clamd - bb1349 */
24
+    int sockd;
25
+    int r;
26
+    char *cur;
27
+    char *bol;
28
+};
29
+
30
+int dconnect(void);
31
+int sendln(int sockd, const char *line, unsigned int len);
32
+void recvlninit(struct RCVLN *s, int sockd);
33
+int recvln(struct RCVLN *s, char **rbol, char **reol);
34
+int serial_client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel);
35
+int parallel_client_scan(const char *file, int scantype, int *infected, int *errors, int maxlevel);
36
+int dsresult(int sockd, int scantype, const char *filename);
37
+#endif