Browse code

freshclam: get detection stats directly from clamd (bb#2312)

Tomasz Kojm authored on 2010/11/05 23:32:51
Showing 16 changed files
... ...
@@ -1,3 +1,7 @@
1
+Fri Nov  5 15:32:22 CET 2010 (tk)
2
+---------------------------------
3
+ * freshclam: get detection stats directly from clamd (bb#2312)
4
+
1 5
 Thu Nov  4 21:12:53 EET 2010 (edwin)
2 6
 ------------------------------------
3 7
  * libclamav/cache.c,c++/bytecode2llvm.cpp}: make cl_load thread safe (bb #2333).
... ...
@@ -31,6 +31,8 @@ clamdscan_SOURCES = \
31 31
     $(top_srcdir)/shared/getopt.h \
32 32
     $(top_srcdir)/shared/actions.c \
33 33
     $(top_srcdir)/shared/actions.h \
34
+    $(top_srcdir)/shared/clamdcom.c \
35
+    $(top_srcdir)/shared/clamdcom.h \
34 36
     clamdscan.c \
35 37
     proto.c \
36 38
     proto.h \
... ...
@@ -81,13 +81,14 @@ am__clamdscan_SOURCES_DIST = $(top_srcdir)/shared/output.c \
81 81
 	$(top_srcdir)/shared/optparser.h $(top_srcdir)/shared/misc.c \
82 82
 	$(top_srcdir)/shared/misc.h $(top_srcdir)/shared/getopt.c \
83 83
 	$(top_srcdir)/shared/getopt.h $(top_srcdir)/shared/actions.c \
84
-	$(top_srcdir)/shared/actions.h clamdscan.c proto.c proto.h \
84
+	$(top_srcdir)/shared/actions.h $(top_srcdir)/shared/clamdcom.c \
85
+	$(top_srcdir)/shared/clamdcom.h clamdscan.c proto.c proto.h \
85 86
 	client.c client.h
86 87
 @BUILD_CLAMD_TRUE@am_clamdscan_OBJECTS = output.$(OBJEXT) \
87 88
 @BUILD_CLAMD_TRUE@	optparser.$(OBJEXT) misc.$(OBJEXT) \
88 89
 @BUILD_CLAMD_TRUE@	getopt.$(OBJEXT) actions.$(OBJEXT) \
89
-@BUILD_CLAMD_TRUE@	clamdscan.$(OBJEXT) proto.$(OBJEXT) \
90
-@BUILD_CLAMD_TRUE@	client.$(OBJEXT)
90
+@BUILD_CLAMD_TRUE@	clamdcom.$(OBJEXT) clamdscan.$(OBJEXT) \
91
+@BUILD_CLAMD_TRUE@	proto.$(OBJEXT) client.$(OBJEXT)
91 92
 clamdscan_OBJECTS = $(am_clamdscan_OBJECTS)
92 93
 clamdscan_LDADD = $(LDADD)
93 94
 AM_V_lt = $(am__v_lt_$(V))
... ...
@@ -295,6 +296,8 @@ top_srcdir = @top_srcdir@
295 295
 @BUILD_CLAMD_TRUE@    $(top_srcdir)/shared/getopt.h \
296 296
 @BUILD_CLAMD_TRUE@    $(top_srcdir)/shared/actions.c \
297 297
 @BUILD_CLAMD_TRUE@    $(top_srcdir)/shared/actions.h \
298
+@BUILD_CLAMD_TRUE@    $(top_srcdir)/shared/clamdcom.c \
299
+@BUILD_CLAMD_TRUE@    $(top_srcdir)/shared/clamdcom.h \
298 300
 @BUILD_CLAMD_TRUE@    clamdscan.c \
299 301
 @BUILD_CLAMD_TRUE@    proto.c \
300 302
 @BUILD_CLAMD_TRUE@    proto.h \
... ...
@@ -408,6 +411,7 @@ distclean-compile:
408 408
 	-rm -f *.tab.c
409 409
 
410 410
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/actions.Po@am__quote@
411
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clamdcom.Po@am__quote@
411 412
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clamdscan.Po@am__quote@
412 413
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@
413 414
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@
... ...
@@ -520,6 +524,22 @@ actions.obj: $(top_srcdir)/shared/actions.c
520 520
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
521 521
 @am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o actions.obj `if test -f '$(top_srcdir)/shared/actions.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/actions.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/actions.c'; fi`
522 522
 
523
+clamdcom.o: $(top_srcdir)/shared/clamdcom.c
524
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT clamdcom.o -MD -MP -MF $(DEPDIR)/clamdcom.Tpo -c -o clamdcom.o `test -f '$(top_srcdir)/shared/clamdcom.c' || echo '$(srcdir)/'`$(top_srcdir)/shared/clamdcom.c
525
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/clamdcom.Tpo $(DEPDIR)/clamdcom.Po
526
+@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@
527
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_srcdir)/shared/clamdcom.c' object='clamdcom.o' libtool=no @AMDEPBACKSLASH@
528
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
529
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o clamdcom.o `test -f '$(top_srcdir)/shared/clamdcom.c' || echo '$(srcdir)/'`$(top_srcdir)/shared/clamdcom.c
530
+
531
+clamdcom.obj: $(top_srcdir)/shared/clamdcom.c
532
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT clamdcom.obj -MD -MP -MF $(DEPDIR)/clamdcom.Tpo -c -o clamdcom.obj `if test -f '$(top_srcdir)/shared/clamdcom.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/clamdcom.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/clamdcom.c'; fi`
533
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/clamdcom.Tpo $(DEPDIR)/clamdcom.Po
534
+@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@
535
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_srcdir)/shared/clamdcom.c' object='clamdcom.obj' libtool=no @AMDEPBACKSLASH@
536
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
537
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o clamdcom.obj `if test -f '$(top_srcdir)/shared/clamdcom.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/clamdcom.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/clamdcom.c'; fi`
538
+
523 539
 mostlyclean-libtool:
524 540
 	-rm -f *.lo
525 541
 
... ...
@@ -56,6 +56,8 @@
56 56
 #include "shared/output.h"
57 57
 #include "shared/misc.h"
58 58
 #include "shared/actions.h"
59
+#include "shared/clamdcom.h"
60
+
59 61
 #include "libclamav/str.h"
60 62
 #include "libclamav/others.h"
61 63
 
... ...
@@ -47,6 +47,7 @@
47 47
 #include "shared/actions.h"
48 48
 #include "shared/output.h"
49 49
 #include "shared/misc.h"
50
+#include "shared/clamdcom.h"
50 51
 
51 52
 #include "proto.h"
52 53
 #include "client.h"
... ...
@@ -77,89 +78,6 @@ int dconnect() {
77 77
     return sockd;
78 78
 }
79 79
 
80
-/* Sends bytes over a socket
81
- * Returns 0 on success */
82
-int sendln(int sockd, const char *line, unsigned int len) {
83
-    while(len) {
84
-	int sent = send(sockd, line, len, 0);
85
-	if(sent <= 0) {
86
-	    if(sent && errno == EINTR) continue;
87
-	    logg("!Can't send to clamd: %s\n", strerror(errno));
88
-	    return 1;
89
-	}
90
-	line += sent;
91
-	len -= sent;
92
-    }
93
-    return 0;
94
-}
95
-
96
-/* Inits a RECVLN struct before it can be used in recvln() - see below */
97
-void recvlninit(struct RCVLN *s, int sockd) {
98
-    s->sockd = sockd;
99
-    s->bol = s->cur = s->buf;
100
-    s->r = 0;
101
-}
102
-
103
-/* Receives a full (terminated with \0) line from a socket
104
- * Sets rbol to the begin of the received line, and optionally 
105
- * reol to the ond of line.
106
- * Should be called repeatedly untill all input is conumed
107
- * Returns 
108
- * - the lenght of the line (a positive number) on success
109
- * - 0 if the connection is closed
110
- * - -1 on error
111
- */
112
-int recvln(struct RCVLN *s, char **rbol, char **reol) {
113
-    char *eol;
114
-
115
-    while(1) {
116
-	if(!s->r) {
117
-	    s->r = recv(s->sockd, s->cur, sizeof(s->buf) - (s->cur - s->buf), 0);
118
-	    if(s->r<=0) {
119
-		if(s->r && errno == EINTR) {
120
-		    s->r = 0;
121
-		    continue;
122
-		}
123
-		if(s->r || s->cur!=s->buf) {
124
-		    *s->cur = '\0';
125
-		    if(strcmp(s->buf, "UNKNOWN COMMAND\n"))
126
-			logg("!Communication error\n");
127
-		    else
128
-			logg("!Command rejected by clamd (wrong clamd version?)\n");
129
-		    return -1;
130
-		}
131
-	        return 0;
132
-	    }
133
-	}
134
-	if((eol = memchr(s->cur, 0, s->r))) {
135
-	    int ret = 0;
136
-	    eol++;
137
-	    s->r -= eol - s->cur;
138
-	    *rbol = s->bol;
139
-	    if(reol) *reol = eol;
140
-	    ret = eol - s->bol;
141
-	    if(s->r)
142
-		s->bol = s->cur = eol;
143
-	    else
144
-		s->bol = s->cur = s->buf;
145
-	    return ret;
146
-	}
147
-	s->r += s->cur - s->bol;
148
-	if(!eol && s->r==sizeof(s->buf)) {
149
-	    logg("!Overlong reply from clamd\n");
150
-	    return -1;
151
-	}
152
-	if(!eol) {
153
-	    if(s->buf != s->bol) { /* old memmove sux */
154
-		memmove(s->buf, s->bol, s->r);
155
-		s->bol = s->buf;
156
-	    }
157
-	    s->cur = &s->bol[s->r];
158
-	    s->r = 0;
159
-	}
160
-    }
161
-}
162
-
163 80
 /* Issues an INSTREAM command to clamd and streams the given file
164 81
  * Returns >0 on success, 0 soft fail, -1 hard fail */
165 82
 static int send_stream(int sockd, const char *filename) {
... ...
@@ -22,18 +22,7 @@
22 22
 #define PROTO_H
23 23
 #include "shared/misc.h"
24 24
 
25
-struct RCVLN {
26
-    char buf[PATH_MAX+1024]; /* FIXME must match that in clamd - bb1349 */
27
-    int sockd;
28
-    int r;
29
-    char *cur;
30
-    char *bol;
31
-};
32
-
33 25
 int dconnect(void);
34
-int sendln(int sockd, const char *line, unsigned int len);
35
-void recvlninit(struct RCVLN *s, int sockd);
36
-int recvln(struct RCVLN *s, char **rbol, char **reol);
37 26
 int serial_client_scan(char *file, int scantype, int *infected, int *err, int maxlevel, int flags);
38 27
 int parallel_client_scan(char *file, int scantype, int *infected, int *err, int maxlevel, int flags);
39 28
 int dsresult(int sockd, int scantype, const char *filename, int *printok, int *errors);
... ...
@@ -67,7 +67,7 @@ Enable verbose logging.
67 67
 Default: no
68 68
 .TP 
69 69
 \fBExtendedDetectionInfo BOOL\fR
70
-Provide additional information about the infected file, such as its size and hash, together with the virus name. It's recommended to enable this option along with SubmitDetectionStats in freshclam.conf.
70
+Log additional information about the infected file, such as its size and hash, together with the virus name.
71 71
 .br 
72 72
 Default: no
73 73
 .TP 
... ...
@@ -160,7 +160,7 @@ Timeout in seconds when reading from database server.
160 160
 Default: 30
161 161
 .TP
162 162
 \fBSubmitDetectionStats STRING\fR
163
-When enabled freshclam will submit statistics to the ClamAV Project about the latest virus detections in your environment. The ClamAV maintainers will then use this data to determine what types of malware are the most detected in the field and in what geographic area they are. This feature requires LogTime and LogFile to be enabled in clamd.conf, it's also recommended to turn on ExtendedDetectionInfo. The path for clamd.conf file must be provided.
163
+When enabled freshclam will submit statistics to the ClamAV Project about the latest virus detections in your environment. The ClamAV maintainers will then use this data to determine what types of malware are the most detected in the field and in what geographic area they are. Freshclam will connect to clamd in order to get the recent statistics. The path for clamd.conf file must be provided.
164 164
 .br
165 165
 Default: disabled
166 166
 .TP
... ...
@@ -51,9 +51,8 @@ Example
51 51
 # Default: no
52 52
 #LogVerbose yes
53 53
 
54
-# Provide additional information about the infected file, such as its
55
-# size and hash, together with the virus name. It's recommended to enable
56
-# this option along with SubmitDetectionStats in freshclam.conf.
54
+# Log additional information about the infected file, such as its
55
+# size and hash, together with the virus name.
57 56
 #ExtendedDetectionInfo yes
58 57
 
59 58
 # This option allows you to save a process identifier of the listening
... ...
@@ -158,8 +158,7 @@ DatabaseMirror database.clamav.net
158 158
 # the latest virus detections in your environment. The ClamAV maintainers
159 159
 # will then use this data to determine what types of malware are the most
160 160
 # detected in the field and in what geographic area they are.
161
-# This feature requires LogTime and LogFile to be enabled in clamd.conf,
162
-# it's also recommended to turn on ExtendedDetectionInfo.
161
+# Freshclam will connect to clamd in order to get recent statistics.
163 162
 # Default: no
164 163
 #SubmitDetectionStats /path/to/clamd.conf
165 164
 
... ...
@@ -32,6 +32,8 @@ freshclam_SOURCES = \
32 32
     $(top_srcdir)/shared/cdiff.h \
33 33
     $(top_srcdir)/shared/tar.c \
34 34
     $(top_srcdir)/shared/tar.h \
35
+    $(top_srcdir)/shared/clamdcom.c \
36
+    $(top_srcdir)/shared/clamdcom.h \
35 37
     freshclam.c \
36 38
     manager.c \
37 39
     manager.h \
... ...
@@ -79,9 +79,9 @@ am__installdirs = "$(DESTDIR)$(bindir)"
79 79
 PROGRAMS = $(bin_PROGRAMS)
80 80
 am_freshclam_OBJECTS = output.$(OBJEXT) optparser.$(OBJEXT) \
81 81
 	getopt.$(OBJEXT) misc.$(OBJEXT) cdiff.$(OBJEXT) tar.$(OBJEXT) \
82
-	freshclam.$(OBJEXT) manager.$(OBJEXT) notify.$(OBJEXT) \
83
-	dns.$(OBJEXT) execute.$(OBJEXT) nonblock.$(OBJEXT) \
84
-	mirman.$(OBJEXT)
82
+	clamdcom.$(OBJEXT) freshclam.$(OBJEXT) manager.$(OBJEXT) \
83
+	notify.$(OBJEXT) dns.$(OBJEXT) execute.$(OBJEXT) \
84
+	nonblock.$(OBJEXT) mirman.$(OBJEXT)
85 85
 freshclam_OBJECTS = $(am_freshclam_OBJECTS)
86 86
 freshclam_LDADD = $(LDADD)
87 87
 AM_V_lt = $(am__v_lt_$(V))
... ...
@@ -291,6 +291,8 @@ freshclam_SOURCES = \
291 291
     $(top_srcdir)/shared/cdiff.h \
292 292
     $(top_srcdir)/shared/tar.c \
293 293
     $(top_srcdir)/shared/tar.h \
294
+    $(top_srcdir)/shared/clamdcom.c \
295
+    $(top_srcdir)/shared/clamdcom.h \
294 296
     freshclam.c \
295 297
     manager.c \
296 298
     manager.h \
... ...
@@ -412,6 +414,7 @@ distclean-compile:
412 412
 	-rm -f *.tab.c
413 413
 
414 414
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cdiff.Po@am__quote@
415
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clamdcom.Po@am__quote@
415 416
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns.Po@am__quote@
416 417
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/execute.Po@am__quote@
417 418
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freshclam.Po@am__quote@
... ...
@@ -545,6 +548,22 @@ tar.obj: $(top_srcdir)/shared/tar.c
545 545
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
546 546
 @am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tar.obj `if test -f '$(top_srcdir)/shared/tar.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/tar.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/tar.c'; fi`
547 547
 
548
+clamdcom.o: $(top_srcdir)/shared/clamdcom.c
549
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT clamdcom.o -MD -MP -MF $(DEPDIR)/clamdcom.Tpo -c -o clamdcom.o `test -f '$(top_srcdir)/shared/clamdcom.c' || echo '$(srcdir)/'`$(top_srcdir)/shared/clamdcom.c
550
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/clamdcom.Tpo $(DEPDIR)/clamdcom.Po
551
+@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@
552
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_srcdir)/shared/clamdcom.c' object='clamdcom.o' libtool=no @AMDEPBACKSLASH@
553
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
554
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o clamdcom.o `test -f '$(top_srcdir)/shared/clamdcom.c' || echo '$(srcdir)/'`$(top_srcdir)/shared/clamdcom.c
555
+
556
+clamdcom.obj: $(top_srcdir)/shared/clamdcom.c
557
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT clamdcom.obj -MD -MP -MF $(DEPDIR)/clamdcom.Tpo -c -o clamdcom.obj `if test -f '$(top_srcdir)/shared/clamdcom.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/clamdcom.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/clamdcom.c'; fi`
558
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/clamdcom.Tpo $(DEPDIR)/clamdcom.Po
559
+@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@
560
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_srcdir)/shared/clamdcom.c' object='clamdcom.obj' libtool=no @AMDEPBACKSLASH@
561
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
562
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o clamdcom.obj `if test -f '$(top_srcdir)/shared/clamdcom.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/clamdcom.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/clamdcom.c'; fi`
563
+
548 564
 mostlyclean-libtool:
549 565
 	-rm -f *.lo
550 566
 
... ...
@@ -75,6 +75,7 @@
75 75
 #include "shared/misc.h"
76 76
 #include "shared/cdiff.h"
77 77
 #include "shared/tar.h"
78
+#include "shared/clamdcom.h"
78 79
 
79 80
 #include "libclamav/clamav.h"
80 81
 #include "libclamav/others.h"
... ...
@@ -429,6 +430,7 @@ static int wwwconnect(const char *server, const char *proxy, int pport, char *ip
429 429
     return -2;
430 430
 }
431 431
 
432
+/*
432 433
 static const char *readblineraw(int fd, char *buf, int bufsize, int filesize, int *bread)
433 434
 {
434 435
 	char *pt;
... ...
@@ -489,6 +491,7 @@ static const char *readbline(int fd, char *buf, int bufsize, int filesize, int *
489 489
 
490 490
     return line;
491 491
 }
492
+*/
492 493
 
493 494
 static unsigned int fmt_base64(char *dest, const char *src, unsigned int len)
494 495
 {
... ...
@@ -559,25 +562,18 @@ static char *proxyauth(const char *user, const char *pass)
559 559
     return auth;
560 560
 }
561 561
 
562
-/*
563
- * TODO:
564
- * - strptime() is most likely not portable enough
565
- */
566 562
 int submitstats(const char *clamdcfg, const struct optstruct *opts)
567 563
 {
568
-	int fd, sd, bread, lread = 0, cnt, ret;
564
+	int sd, clamsockd, bread, cnt, ret;
569 565
 	char post[SUBMIT_MIN_ENTRIES * 256 + 512];
570 566
 	char query[SUBMIT_MIN_ENTRIES * 256];
571
-	char buff[512], statsdat[512], newstatsdat[512], uastr[128];
572
-	char logfile[256], fbuff[FILEBUFF];
573
-	char *pt, *pt2, *auth = NULL;
574
-	const char *line, *country = NULL, *user, *proxy = NULL, *hostid = NULL;
575
-	struct optstruct *clamdopt;
567
+	char uastr[128], *line;
568
+	char *pt, *auth = NULL;
569
+	const char *country = NULL, *user, *proxy = NULL, *hostid = NULL;
576 570
 	const struct optstruct *opt;
577
-	struct stat sb;
578
-	struct tm tms;
579
-	time_t epoch;
580 571
 	unsigned int qcnt, entries, submitted = 0, permfail = 0, port = 0;
572
+        struct RCVLN rcv;
573
+	const char *tokens[5];
581 574
 
582 575
 
583 576
     if((opt = optget(opts, "DetectionStatsCountry"))->enabled) {
... ...
@@ -596,67 +592,6 @@ int submitstats(const char *clamdcfg, const struct optstruct *opts)
596 596
 	hostid = opt->strarg;
597 597
     }
598 598
 
599
-    if(!(clamdopt = optparse(clamdcfg, 0, NULL, 1, OPT_CLAMD, 0, NULL))) {
600
-	logg("!SubmitDetectionStats: Can't open or parse configuration file %s\n", clamdcfg);
601
-	return 56;
602
-    }
603
-
604
-    if(!(opt = optget(clamdopt, "LogFile"))->enabled) {
605
-	logg("!SubmitDetectionStats: LogFile needs to be enabled in %s\n", clamdcfg);
606
-	logg("SubmitDetectionStats: Please consider enabling ExtendedDetectionInfo\n");
607
-	optfree(clamdopt);
608
-	return 56;
609
-    }
610
-    strncpy(logfile, opt->strarg, sizeof(logfile));
611
-    logfile[sizeof(logfile) - 1] = 0;
612
-
613
-    if(!optget(clamdopt, "LogTime")->enabled) {
614
-	logg("!SubmitDetectionStats: LogTime needs to be enabled in %s\n", clamdcfg);
615
-	optfree(clamdopt);
616
-	return 56;
617
-    }
618
-    optfree(clamdopt);
619
-
620
-    if((fd = open("stats.dat", O_RDONLY|O_BINARY)) != -1) {
621
-	if((bread = read(fd, statsdat, sizeof(statsdat) - 1)) == -1) {
622
-	    logg("^SubmitDetectionStats: Can't read stats.dat\n");
623
-	    bread = 0;
624
-	}
625
-	statsdat[bread] = 0;
626
-	close(fd);
627
-    } else {
628
-	*statsdat = 0;
629
-    }
630
-
631
-    if((fd = open(logfile, O_RDONLY|O_BINARY)) == -1) {
632
-	logg("!SubmitDetectionStats: Can't open %s for reading\n", logfile);
633
-	return 56;
634
-    }
635
-
636
-    if(fstat(fd, &sb) == -1) {
637
-	logg("!SubmitDetectionStats: fstat() failed\n");
638
-	close(fd);
639
-	return 56;
640
-    }
641
-
642
-    while((line = readbline(fd, fbuff, FILEBUFF, sb.st_size, &lread)))
643
-	if(strlen(line) >= 32 && !strcmp(&line[strlen(line) - 6], " FOUND"))
644
-	    break;
645
-
646
-    if(!line) {
647
-	logg("SubmitDetectionStats: No detection records found\n");
648
-	close(fd);
649
-	return 1;
650
-    }
651
-
652
-    if(*statsdat && !strcmp(line, statsdat)) {
653
-	logg("SubmitDetectionStats: No new detection records found\n");
654
-	close(fd);
655
-	return 1;
656
-    } else {
657
-	strncpy(newstatsdat, line, sizeof(newstatsdat));
658
-    }
659
-
660 599
     if((opt = optget(opts, "HTTPUserAgent"))->enabled)
661 600
         strncpy(uastr, opt->strarg, sizeof(uastr));
662 601
     else
... ...
@@ -672,14 +607,11 @@ int submitstats(const char *clamdcfg, const struct optstruct *opts)
672 672
 	    user = opt->strarg;
673 673
 	    if(!(opt = optget(opts, "HTTPProxyPassword"))->enabled) {
674 674
 		logg("!SubmitDetectionStats: HTTPProxyUsername requires HTTPProxyPassword\n");
675
-		close(fd);
676 675
 		return 56;
677 676
 	    }
678 677
 	    auth = proxyauth(user, opt->strarg);
679
-	    if(!auth) {
680
-		close(fd);
678
+	    if(!auth)
681 679
 		return 56;
682
-	    }
683 680
 	}
684 681
 
685 682
 	if((opt = optget(opts, "HTTPProxyPort"))->enabled)
... ...
@@ -688,50 +620,27 @@ int submitstats(const char *clamdcfg, const struct optstruct *opts)
688 688
 	logg("*Connecting via %s\n", proxy);
689 689
     }
690 690
 
691
+    if((clamsockd = clamd_connect(clamdcfg, "SubmitDetectionStats")) < 0)
692
+	return 52;
693
+
694
+    recvlninit(&rcv, clamsockd);
695
+    if(sendln(clamsockd, "zDETSTATS", 10)) {
696
+        closesocket(clamsockd);
697
+	return 52;
698
+    }
699
+
691 700
     ret = 0;
692 701
     memset(query, 0, sizeof(query));
693 702
     qcnt = 0;
694 703
     entries = 0;
695
-    do {
696
-	if(strlen(line) < 32 || strcmp(&line[strlen(line) - 6], " FOUND"))
697
-	    continue;
698
-
699
-	if(*statsdat && !strcmp(line, statsdat))
700
-	    break;
701
-
702
-	strncpy(buff, line, sizeof(buff));
703
-	buff[sizeof(buff) - 1] = 0;
704
-	if(!(pt = strstr(buff, " -> "))) {
705
-	    logg("*SubmitDetectionStats: Skipping detection entry logged without time\b");
706
-	    continue;
707
-	}
708
-	*pt = 0;
709
-	pt += 4;
710
-
711
-	tms.tm_isdst = -1;
712
-	if(!strptime(buff, "%a %b  %d %H:%M:%S %Y", &tms) || (epoch = mktime(&tms)) == -1) {
713
-	    logg("!SubmitDetectionStats: Failed to convert date string\n");
714
-	    ret = 1;
715
-	    break;
716
-	}
717
-
718
-	pt2 = &pt[strlen(pt) - 6];
719
-	*pt2 = 0;
720
-
721
-	if(!(pt2 = strrchr(pt, ' ')) || pt2[-1] != ':') {
722
-	    logg("!SubmitDetectionStats: Incorrect format of the log file (1)\n");
723
-	    ret = 1;
704
+    while(recvln(&rcv, &line, NULL) > 0) {
705
+        if(cli_strtokenize(line, ':', 5, tokens) != 5) {
706
+	    logg("!SubmitDetectionStats: Invalid data format\n");
707
+	    ret = 52;
724 708
 	    break;
725 709
 	}
726
-	pt2[-1] = 0;
727
-	pt2++;
728 710
 
729
-	if((pt = strrchr(pt, *PATHSEP)))
730
-	    *pt++ = 0;
731
-	if(!pt)
732
-	    pt = (char*) "NOFNAME";
733
-
734
-	qcnt += snprintf(&query[qcnt], sizeof(query) - qcnt, "ts[]=%u&fname[]=%s&virus[]=%s&", (unsigned int) epoch, pt, pt2);
711
+	qcnt += snprintf(&query[qcnt], sizeof(query) - qcnt, "ts[]=%s&fname[]=%s&fsize[]=%s&md5[]=%s&virus[]=%s&", tokens[0], tokens[4], tokens[2], tokens[1], tokens[3]);
735 712
 	entries++;
736 713
 
737 714
 	if(entries == SUBMIT_MIN_ENTRIES) {
... ...
@@ -741,7 +650,6 @@ int submitstats(const char *clamdcfg, const struct optstruct *opts)
741 741
 		ret = 52;
742 742
 		break;
743 743
 	    }
744
-
745 744
 	    query[sizeof(query) - 1] = 0;
746 745
 	    if(mdprintf(sd,
747 746
 		"POST http://stats.clamav.net/submit.php HTTP/1.0\r\n"
... ...
@@ -812,30 +720,23 @@ int submitstats(const char *clamdcfg, const struct optstruct *opts)
812 812
 
813 813
 	    break;
814 814
 	}
815
-
816
-    } while((line = readbline(fd, fbuff, FILEBUFF, sb.st_size, &lread)));
817
-
818
-    close(fd);
815
+    }
816
+    closesocket(clamsockd);
819 817
     if(auth)
820 818
 	free(auth);
821 819
 
822
-    if(submitted || permfail) {
823
-	if((fd = open("stats.dat", O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600)) == -1) {
824
-	    logg("^SubmitDetectionStats: Can't open stats.dat for writing\n");
825
-	} else {
826
-	    if((bread = write(fd, newstatsdat, sizeof(newstatsdat))) != sizeof(newstatsdat))
827
-		logg("^SubmitDetectionStats: Can't write to stats.dat\n");
828
-	    close(fd);
829
-	}
830
-    }
831
-
832 820
     if(ret == 0) {
833
-	if(!submitted)
821
+	if(!submitted) {
834 822
 	    logg("SubmitDetectionStats: Not enough recent data for submission\n");
835
-	else
823
+	} else {
836 824
 	    logg("SubmitDetectionStats: Submitted %u records\n", submitted);
825
+	    if((clamsockd = clamd_connect(clamdcfg, "SubmitDetectionStats")) != -1) {
826
+		sendln(clamsockd, "DETSTATSCLEAR", 14);
827
+		recv(clamsockd, query, sizeof(query), 0);
828
+		closesocket(clamsockd);
829
+	    }
830
+	}
837 831
     }
838
-
839 832
     return ret;
840 833
 }
841 834
 
... ...
@@ -1970,7 +1871,7 @@ static int updatedb(const char *dbname, const char *hostname, char *ip, int *sig
1970 1970
 static int updatecustomdb(const char *url, int *signo, const struct optstruct *opts, char *localip, int logerr)
1971 1971
 {
1972 1972
 	const struct optstruct *opt;
1973
-	unsigned int port = 0, newsigs = 0, sigs = 0;
1973
+	unsigned int port = 0, sigs = 0;
1974 1974
 	int ret;
1975 1975
 	char *pt, *host, urlcpy[256], *newfile = NULL, mtime[36], *newfile2;
1976 1976
 	const char *proxy = NULL, *user = NULL, *pass = NULL, *uas = NULL, *rpath, *dbname;
... ...
@@ -39,11 +39,12 @@
39 39
 
40 40
 #include "shared/optparser.h"
41 41
 #include "shared/output.h"
42
+#include "shared/clamdcom.h"
43
+
42 44
 #include "notify.h"
43 45
 
44
-int notify(const char *cfgfile)
46
+int clamd_connect(const char *cfgfile, const char *option)
45 47
 {
46
-	char buff[20];
47 48
 #ifndef	_WIN32
48 49
 	struct sockaddr_un server;
49 50
 #endif
... ...
@@ -63,8 +64,8 @@ int notify(const char *cfgfile)
63 63
 
64 64
 
65 65
     if((opts = optparse(cfgfile, 0, NULL, 1, OPT_CLAMD, 0, NULL)) == NULL) {
66
-	logg("^Clamd was NOT notified: Can't find or parse configuration file %s\n", cfgfile);
67
-	return 1;
66
+	logg("!%s: Can't find or parse configuration file %s\n", option, cfgfile);
67
+	return -11;
68 68
     }
69 69
 
70 70
 #ifndef	_WIN32
... ...
@@ -78,7 +79,7 @@ int notify(const char *cfgfile)
78 78
 	    logg("^Clamd was NOT notified: Can't create socket endpoint for %s\n", opt->strarg);
79 79
 	    perror("socket()");
80 80
 	    optfree(opts);
81
-	    return 1;
81
+	    return -1;
82 82
 	}
83 83
 
84 84
 	if(connect(sockd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
... ...
@@ -86,7 +87,7 @@ int notify(const char *cfgfile)
86 86
 	    logg("^Clamd was NOT notified: Can't connect to clamd through %s\n", opt->strarg);
87 87
 	    perror("connect()");
88 88
 	    optfree(opts);
89
-	    return 1;
89
+	    return -11;
90 90
 	}
91 91
 
92 92
     } else
... ...
@@ -113,36 +114,36 @@ int notify(const char *cfgfile)
113 113
 	ret = getaddrinfo(addr, port, &hints, &res);
114 114
 
115 115
 	if(ret) {
116
-	    logg("^Clamd was NOT notified: Can't resolve hostname %s (%s)\n", addr ? addr : "", (ret == EAI_SYSTEM) ? strerror(errno) : gai_strerror(ret));
116
+	    logg("!%s: Can't resolve hostname %s (%s)\n", option, addr ? addr : "", (ret == EAI_SYSTEM) ? strerror(errno) : gai_strerror(ret));
117 117
 	    optfree(opts);
118
-	    return 1;
118
+	    return -1;
119 119
 	}
120 120
 
121 121
 	if((sockd = socket(res->ai_family, SOCK_STREAM, 0)) < 0) {
122 122
 	    perror("socket()");
123
-	    logg("^Clamd was NOT notified: Can't create TCP socket\n");
123
+	    logg("!%s: Can't create TCP socket\n", option);
124 124
 	    optfree(opts);
125 125
 	    freeaddrinfo(res);
126
-	    return 1;
126
+	    return -1;
127 127
 	}
128 128
 
129 129
 	if(connect(sockd, res->ai_addr, res->ai_addrlen) == -1) {
130 130
 	    perror("connect()");
131 131
 	    closesocket(sockd);
132
-	    logg("^Clamd was NOT notified: Can't connect to clamd on %s:%s\n", addr ? addr : "localhost", port);
132
+	    logg("!%s: Can't connect to clamd on %s:%s\n", option, addr ? addr : "localhost", port);
133 133
 	    optfree(opts);
134 134
 	    freeaddrinfo(res);
135
-	    return 1;
135
+	    return -1;
136 136
 	}
137 137
 	freeaddrinfo(res);
138 138
 
139 139
 #else /* IPv4 */
140 140
 
141 141
 	if((sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
142
-	    logg("^Clamd was NOT notified: Can't create TCP socket\n");
142
+	    logg("!%s: Can't create TCP socket\n", option);
143 143
 	    perror("socket()");
144 144
 	    optfree(opts);
145
-	    return 1;
145
+	    return -1;
146 146
 	}
147 147
 
148 148
 	server2.sin_family = AF_INET;
... ...
@@ -154,7 +155,7 @@ int notify(const char *cfgfile)
154 154
 		logg("^Clamd was NOT notified: Can't resolve hostname '%s'\n", opt->strarg);
155 155
 		optfree(opts);
156 156
 		closesocket(sockd);
157
-		return 1;
157
+		return -1;
158 158
 	    }
159 159
 	    server2.sin_addr = *(struct in_addr *) he->h_addr_list[0];
160 160
 	} else
... ...
@@ -167,39 +168,48 @@ int notify(const char *cfgfile)
167 167
 		    inet_ntoa(server2.sin_addr), ntohs(server2.sin_port));
168 168
 	    perror("connect()");
169 169
 	    optfree(opts);
170
-	    return 1;
170
+	    return -1;
171 171
 	}
172 172
 
173 173
 #endif
174 174
 
175 175
     } else {
176
-	logg("^Clamd was NOT notified: No socket specified in %s\n", cfgfile);
176
+	logg("!%s: No communication socket specified in %s\n", option, cfgfile);
177 177
 	optfree(opts);
178 178
 	return 1;
179 179
     }
180 180
 
181
-    if(send(sockd, "RELOAD", 6, 0) < 0) {
182
-	logg("^Clamd was NOT notified: Could not write to %s socket\n", socktype);
183
-	perror("write()");
181
+    optfree(opts);
182
+    return sockd;
183
+}
184
+
185
+int notify(const char *cfgfile)
186
+{
187
+	char buff[20];
188
+	int sockd, bread;
189
+	const char *socktype;
190
+
191
+    if((sockd = clamd_connect(cfgfile, "NotifyClamd")) < 0)
192
+	return 1;
193
+
194
+    if(sendln(sockd, "RELOAD", 7) < 0) {
195
+	logg("!NotifyClamd: Could not write to clamd socket\n");
196
+	perror("send()");
184 197
 	closesocket(sockd);
185
-	optfree(opts);
186 198
 	return 1;
187 199
     }
188 200
 
189
-    /* TODO: Handle timeout */
190 201
     memset(buff, 0, sizeof(buff));
191
-    if((bread = recv(sockd, buff, sizeof(buff), 0)) > 0)
202
+    if((bread = recv(sockd, buff, sizeof(buff), 0)) > 0) {
192 203
 	if(!strstr(buff, "RELOADING")) {
193
-	    logg("^Clamd was NOT notified: Unknown answer from clamd: '%s'\n", buff);
204
+	    logg("!NotifyClamd: Unknown answer from clamd: '%s'\n", buff);
194 205
 	    closesocket(sockd);
195
-	    optfree(opts);
196 206
 	    return 1;
197 207
 	}
208
+    }
198 209
 
199 210
     closesocket(sockd);
200 211
     logg("Clamd successfully notified about the update.\n");
201
-    optfree(opts);
202 212
     return 0;
203 213
 }
204
-
205 214
 #endif
... ...
@@ -20,5 +20,6 @@
20 20
 #define __NOTIFY_H
21 21
 
22 22
 int notify(const char *cfgfile);
23
+int clamd_connect(const char *cfgfile, const char *option);
23 24
 
24 25
 #endif
... ...
@@ -177,7 +177,7 @@ const struct clam_option __clam_options[] = {
177 177
 
178 178
     { "LogVerbose", NULL, 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM | OPT_MILTER, "Enable verbose logging.", "yes" },
179 179
 
180
-    { "ExtendedDetectionInfo", NULL, 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD, "Provide additional information about the infected file, such as its\nsize and hash, together with the virus name.", "yes" },
180
+    { "ExtendedDetectionInfo", NULL, 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD, "Log additional information about the infected file, such as its\nsize and hash, together with the virus name.", "yes" },
181 181
 
182 182
     { "PidFile", "pid", 'p', TYPE_STRING, NULL, -1, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM | OPT_MILTER, "Save the process ID to a file.", "/var/run/clam.pid" },
183 183
 
... ...
@@ -390,7 +390,7 @@ const struct clam_option __clam_options[] = {
390 390
 
391 391
     { "ReceiveTimeout", NULL, 0, TYPE_NUMBER, MATCH_NUMBER, 30, NULL, 0, OPT_FRESHCLAM, "Timeout in seconds when reading from database server.", "30" },
392 392
 
393
-    { "SubmitDetectionStats", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM, "When enabled freshclam will submit statistics to the ClamAV Project about\nthe latest virus detections in your environment. The ClamAV maintainers\nwill then use this data to determine what types of malware are the most\ndetected in the field and in what geographic area they are.\nThis feature requires LogTime and LogFile to be enabled in clamd.conf.", "/path/to/clamd.conf" },
393
+    { "SubmitDetectionStats", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM, "When enabled freshclam will submit statistics to the ClamAV Project about\nthe latest virus detections in your environment. The ClamAV maintainers\nwill then use this data to determine what types of malware are the most\ndetected in the field and in what geographic area they are.\nFreshclam will connect to clamd in order to get recent statistics.", "/path/to/clamd.conf" },
394 394
 
395 395
     { "DetectionStatsCountry", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM, "Country of origin of malware/detection statistics (for statistical\npurposes only). The statistics collector at ClamAV.net will look up\nyour IP address to determine the geographical origin of the malware\nreported by your installation. If this installation is mainly used to\nscan data which comes from a different location, please enable this\noption and enter a two-letter code (see http://www.iana.org/domains/root/db/)\nof the country of origin.", "country-code" },
396 396