source: clamav/trunk/freshclam/manager.c@ 338

Last change on this file since 338 was 338, checked in by Yuri Dario, 14 years ago

clamav: update trunk to 0.97.2.

File size: 64.0 KB
Line 
1/*
2 * Copyright (C) 2002 - 2006 Tomasz Kojm <tkojm@clamav.net>
3 * HTTP/1.1 compliance by Arkadiusz Miskiewicz <misiek@pld.org.pl>
4 * Proxy support by Nigel Horne <njh@bandsman.co.uk>
5 * Proxy authorization support by Gernot Tenchio <g.tenchio@telco-tech.de>
6 * (uses fmt_base64() from libowfat (http://www.fefe.de))
7 * CDIFF code (C) 2006 Sensory Networks, Inc.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1301, USA.
23 */
24
25#if HAVE_CONFIG_H
26#include "clamav-config.h"
27#endif
28
29/* for strptime, it is POSIX, but defining _XOPEN_SOURCE to 600
30 * fails on Solaris because it would require a c99 compiler,
31 * 500 fails completely on Solaris, and FreeBSD, and w/o _XOPEN_SOURCE
32 * strptime is not defined on Linux */
33#define _GNU_SOURCE
34#define __EXTENSIONS
35
36#include <stdio.h>
37#include <stdlib.h>
38#ifdef HAVE_UNISTD_H
39#include <unistd.h>
40#endif
41#include <string.h>
42#ifdef HAVE_STRINGS_H
43#include <strings.h>
44#endif
45#include <ctype.h>
46#ifndef _WIN32
47#include <netinet/in.h>
48#include <netdb.h>
49#include <arpa/inet.h>
50#include <sys/socket.h>
51#include <sys/time.h>
52#endif
53#include <sys/types.h>
54#include <time.h>
55#include <fcntl.h>
56#ifndef _WIN32
57#include <sys/wait.h>
58#endif
59#include <sys/stat.h>
60#include <dirent.h>
61#include <errno.h>
62#include <zlib.h>
63
64#include "target.h"
65
66#include "manager.h"
67#include "notify.h"
68#include "dns.h"
69#include "execute.h"
70#include "nonblock.h"
71#include "mirman.h"
72
73#include "shared/optparser.h"
74#include "shared/output.h"
75#include "shared/misc.h"
76#include "shared/cdiff.h"
77#include "shared/tar.h"
78#include "shared/clamdcom.h"
79
80#include "libclamav/clamav.h"
81#include "libclamav/others.h"
82#include "libclamav/str.h"
83#include "libclamav/cvd.h"
84#include "libclamav/regex_list.h"
85
86extern char updtmpdir[512], dbdir[512];
87
88#define CHDIR_ERR(x) \
89 if(chdir(x) == -1) \
90 logg("!Can't chdir to %s\n", x);
91
92#ifndef HAVE_GETADDRINFO
93static const char *ghbn_err(int err) /* hstrerror() */
94{
95 switch(err) {
96 case HOST_NOT_FOUND:
97 return "Host not found";
98
99 case NO_DATA:
100 return "No IP address";
101
102 case NO_RECOVERY:
103 return "Unrecoverable DNS error";
104
105 case TRY_AGAIN:
106 return "Temporary DNS error";
107
108 default:
109 return "Unknown error";
110 }
111}
112#endif
113
114static int getclientsock(const char *localip, int prot)
115{
116 int socketfd = -1;
117
118#ifdef SUPPORT_IPv6
119 if(prot == AF_INET6)
120 socketfd = socket(AF_INET6, SOCK_STREAM, 0);
121 else
122#endif
123 socketfd = socket(AF_INET, SOCK_STREAM, 0);
124 if(socketfd < 0) {
125 logg("!Can't create new socket\n");
126 return -1;
127 }
128
129 if(localip) {
130#ifdef HAVE_GETADDRINFO
131 struct addrinfo *res;
132 int ret;
133
134 ret = getaddrinfo(localip, NULL, NULL, &res);
135 if(ret) {
136 logg("!Could not resolve local ip address '%s': %s\n", localip, gai_strerror(ret));
137 logg("^Using standard local ip address and port for fetching.\n");
138 } else {
139 char ipaddr[46];
140
141 if(bind(socketfd, res->ai_addr, res->ai_addrlen) != 0) {
142 logg("!Could not bind to local ip address '%s': %s\n", localip, strerror(errno));
143 logg("^Using default client ip.\n");
144 } else {
145 void *addr;
146
147#ifdef SUPPORT_IPv6
148 if(res->ai_family == AF_INET6)
149 addr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
150 else
151#endif
152 addr = &((struct sockaddr_in *) res->ai_addr)->sin_addr;
153
154 if(inet_ntop(res->ai_family, addr, ipaddr, sizeof(ipaddr)))
155 logg("*Using ip '%s' for fetching.\n", ipaddr);
156 }
157 freeaddrinfo(res);
158 }
159
160#else /* IPv4 */
161 struct hostent *he;
162
163 if(!(he = gethostbyname(localip))) {
164 logg("!Could not resolve local ip address '%s': %s\n", localip, ghbn_err(h_errno));
165 logg("^Using standard local ip address and port for fetching.\n");
166 } else {
167 struct sockaddr_in client;
168 unsigned char *ia;
169 char ipaddr[16];
170
171 memset((char *) &client, 0, sizeof(client));
172 client.sin_family = AF_INET;
173 client.sin_addr = *(struct in_addr *) he->h_addr_list[0];
174 if(bind(socketfd, (struct sockaddr *) &client, sizeof(struct sockaddr_in)) != 0) {
175 logg("!Could not bind to local ip address '%s': %s\n", localip, strerror(errno));
176 logg("^Using default client ip.\n");
177 } else {
178 ia = (unsigned char *) he->h_addr_list[0];
179 sprintf(ipaddr, "%u.%u.%u.%u", ia[0], ia[1], ia[2], ia[3]);
180 logg("*Using ip '%s' for fetching.\n", ipaddr);
181 }
182 }
183#endif
184 }
185
186 return socketfd;
187}
188
189#ifdef HAVE_GETADDRINFO
190static int qcompare(const void *a, const void *b)
191{
192 return (*(const struct addrinfo **) a)->ai_flags - (*(const struct addrinfo **) b)->ai_flags;
193}
194#endif
195
196static int wwwconnect(const char *server, const char *proxy, int pport, char *ip, const char *localip, int ctimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist)
197{
198 int socketfd, port, ret;
199 unsigned int ips = 0, ignored = 0, i;
200#ifdef HAVE_GETADDRINFO
201 struct addrinfo hints, *res = NULL, *rp, *loadbal_rp = NULL, *addrs[128];
202 char port_s[6], loadbal_ipaddr[46];
203 uint32_t loadbal = 1, minsucc = 0xffffffff, minfail = 0xffffffff, addrnum = 0;
204 int ipv4start = -1, ipv4end = -1;
205 struct mirdat_ip *md;
206#else
207 struct sockaddr_in name;
208 struct hostent *host;
209 unsigned char *ia;
210#endif
211 char ipaddr[46];
212 const char *hostpt;
213
214 if(ip)
215 strcpy(ip, "UNKNOWN");
216
217 if(proxy) {
218 hostpt = proxy;
219
220 if(!(port = pport)) {
221 const struct servent *webcache = getservbyname("webcache", "TCP");
222
223 if(webcache)
224 port = ntohs(webcache->s_port);
225 else
226 port = 8080;
227
228 endservent();
229 }
230
231 } else {
232 hostpt = server;
233 port = 80;
234 }
235
236#ifdef HAVE_GETADDRINFO
237 memset(&hints, 0, sizeof(hints));
238#ifdef SUPPORT_IPv6
239 hints.ai_family = AF_UNSPEC;
240#else
241 hints.ai_family = AF_INET;
242#endif
243 hints.ai_socktype = SOCK_STREAM;
244 snprintf(port_s, sizeof(port_s), "%d", port);
245 port_s[sizeof(port_s) - 1] = 0;
246 ret = getaddrinfo(hostpt, port_s, &hints, &res);
247 if(ret) {
248 logg("%cCan't get information about %s: %s\n", logerr ? '!' : '^', hostpt, gai_strerror(ret));
249 return -1;
250 }
251
252 for(rp = res; rp && addrnum < 128; rp = rp->ai_next) {
253 rp->ai_flags = cli_rndnum(1024);
254 addrs[addrnum] = rp;
255 if(rp->ai_family == AF_INET) {
256 if(ipv4start == -1)
257 ipv4start = addrnum;
258 } else if(ipv4end == -1 && ipv4start != -1) {
259 ipv4end = addrnum - 1;
260 }
261 if(!rp->ai_next && ipv4end == -1)
262 ipv4end = addrnum;
263 addrnum++;
264 }
265 if(ipv4end != -1 && ipv4start != -1 && ipv4end - ipv4start + 1 > 1)
266 qsort(&addrs[ipv4start], ipv4end - ipv4start + 1, sizeof(struct addrinfo *), qcompare);
267
268 for(i = 0; i < addrnum; ) {
269 void *addr;
270
271 rp = addrs[i];
272 ips++;
273#ifdef SUPPORT_IPv6
274 if(rp->ai_family == AF_INET6)
275 addr = &((struct sockaddr_in6 *) rp->ai_addr)->sin6_addr;
276 else
277#endif
278 addr = &((struct sockaddr_in *) rp->ai_addr)->sin_addr;
279
280 if(!inet_ntop(rp->ai_family, addr, ipaddr, sizeof(ipaddr))) {
281 logg("%cinet_ntop() failed\n", logerr ? '!' : '^');
282 freeaddrinfo(res);
283 return -1;
284 }
285
286 if(mdat && (ret = mirman_check(addr, rp->ai_family, mdat, &md))) {
287 if(ret == 1)
288 logg("*Ignoring mirror %s (due to previous errors)\n", ipaddr);
289 else
290 logg("*Ignoring mirror %s (has connected too many times with an outdated version)\n", ipaddr);
291
292 ignored++;
293 if(!loadbal || i + 1 < addrnum) {
294 i++;
295 continue;
296 }
297 }
298
299 if(mdat && loadbal) {
300 if(!ret) {
301 if(!md) {
302 loadbal_rp = rp;
303 strncpy(loadbal_ipaddr, ipaddr, sizeof(loadbal_ipaddr));
304 } else {
305 if(md->succ <= minsucc && md->fail <= minfail) {
306 minsucc = md->succ;
307 minfail = md->fail;
308 loadbal_rp = rp;
309 strncpy(loadbal_ipaddr, ipaddr, sizeof(loadbal_ipaddr));
310 }
311 if(i + 1 < addrnum) {
312 i++;
313 continue;
314 }
315 }
316 }
317
318 if(!loadbal_rp) {
319 if(i + 1 == addrnum) {
320 loadbal = 0;
321 i = 0;
322 }
323 continue;
324 }
325 rp = loadbal_rp;
326 strncpy(ipaddr, loadbal_ipaddr, sizeof(ipaddr));
327#ifdef SUPPORT_IPv6
328 if(rp->ai_family == AF_INET6)
329 addr = &((struct sockaddr_in6 *) rp->ai_addr)->sin6_addr;
330 else
331#endif
332 addr = &((struct sockaddr_in *) rp->ai_addr)->sin_addr;
333 } else if(loadbal_rp == rp) {
334 i++;
335 continue;
336 }
337
338 if(ip)
339 strcpy(ip, ipaddr);
340
341 if(rp != loadbal_rp && rp != addrs[0])
342 logg("Trying host %s (%s)...\n", hostpt, ipaddr);
343
344 socketfd = getclientsock(localip, rp->ai_family);
345 if(socketfd < 0) {
346 freeaddrinfo(res);
347 return -1;
348 }
349
350#ifdef SO_ERROR
351 if(wait_connect(socketfd, rp->ai_addr, rp->ai_addrlen, ctimeout) == -1) {
352#else
353 if(connect(socketfd, rp->ai_addr, rp->ai_addrlen) == -1) {
354#endif
355 logg("Can't connect to port %d of host %s (IP: %s)\n", port, hostpt, ipaddr);
356 closesocket(socketfd);
357 if(loadbal) {
358 loadbal = 0;
359 i = 0;
360 } else i++;
361 continue;
362 } else {
363 if(mdat) {
364 if(rp->ai_family == AF_INET)
365 mdat->currip[0] = *((uint32_t *) addr);
366 else
367 memcpy(mdat->currip, addr, 4 * sizeof(uint32_t));
368 mdat->af = rp->ai_family;
369 }
370 freeaddrinfo(res);
371 return socketfd;
372 }
373 i++;
374 }
375 freeaddrinfo(res);
376
377#else /* IPv4 */
378
379 if((host = gethostbyname(hostpt)) == NULL) {
380 logg("%cCan't get information about %s: %s\n", logerr ? '!' : '^', hostpt, ghbn_err(h_errno));
381 return -1;
382 }
383
384 for(i = 0; host->h_addr_list[i] != 0; i++) {
385 /* this dirty hack comes from pink - Nosuid TCP/IP ping 1.6 */
386 ia = (unsigned char *) host->h_addr_list[i];
387 sprintf(ipaddr, "%u.%u.%u.%u", ia[0], ia[1], ia[2], ia[3]);
388
389 ips++;
390 if(mdat && (ret = mirman_check(&((struct in_addr *) ia)->s_addr, AF_INET, mdat, NULL))) {
391 if(ret == 1)
392 logg("*Ignoring mirror %s (due to previous errors)\n", ipaddr);
393 else
394 logg("*Ignoring mirror %s (has connected too many times with an outdated version)\n", ipaddr);
395 ignored++;
396 continue;
397 }
398
399 if(ip)
400 strcpy(ip, ipaddr);
401
402 if(i > 0)
403 logg("Trying host %s (%s)...\n", hostpt, ipaddr);
404
405 memset ((char *) &name, 0, sizeof(name));
406 name.sin_family = AF_INET;
407 name.sin_addr = *((struct in_addr *) host->h_addr_list[i]);
408 name.sin_port = htons(port);
409
410 socketfd = getclientsock(localip, AF_INET);
411 if(socketfd < 0)
412 return -1;
413
414#ifdef SO_ERROR
415 if(wait_connect(socketfd, (struct sockaddr *) &name, sizeof(struct sockaddr_in), ctimeout) == -1) {
416#else
417 if(connect(socketfd, (struct sockaddr *) &name, sizeof(struct sockaddr_in)) == -1) {
418#endif
419 logg("Can't connect to port %d of host %s (IP: %s)\n", port, hostpt, ipaddr);
420 closesocket(socketfd);
421 continue;
422 } else {
423 if(mdat) {
424 mdat->currip[0] = ((struct in_addr *) ia)->s_addr;
425 mdat->af = AF_INET;
426 }
427 return socketfd;
428 }
429 }
430#endif
431
432 if(mdat && can_whitelist && ips && (ips == ignored))
433 mirman_whitelist(mdat, 1);
434
435 return -2;
436}
437
438/*
439static const char *readblineraw(int fd, char *buf, int bufsize, int filesize, int *bread)
440{
441 char *pt;
442 int ret, end;
443
444 if(!*bread) {
445 if(bufsize < filesize)
446 lseek(fd, 1 - bufsize, SEEK_END);
447 *bread = read(fd, buf, bufsize - 1);
448 if(!*bread || *bread == -1)
449 return NULL;
450 buf[*bread] = 0;
451 }
452
453 pt = strrchr(buf, '\n');
454 if(!pt)
455 return NULL;
456 *pt = 0;
457 pt = strrchr(buf, '\n');
458 if(pt) {
459 return ++pt;
460 } else if(*bread == filesize) {
461 return buf;
462 } else {
463 *bread -= strlen(buf) + 1;
464 end = filesize - *bread;
465 if(end < bufsize) {
466 if((ret = lseek(fd, 0, SEEK_SET)) != -1)
467 ret = read(fd, buf, end);
468 } else {
469 if((ret = lseek(fd, end - bufsize + 1, SEEK_SET)) != -1)
470 ret = read(fd, buf, bufsize - 1);
471 }
472 if(!ret || ret == -1)
473 return NULL;
474 buf[ret] = 0;
475 *bread += ret;
476 pt = strrchr(buf, '\n');
477 if(!pt)
478 return buf;
479 *pt = 0;
480 pt = strrchr(buf, '\n');
481 if(pt)
482 return ++pt;
483 else if(strlen(buf))
484 return buf;
485 else
486 return NULL;
487 }
488}
489
490static const char *readbline(int fd, char *buf, int bufsize, int filesize, int *bread)
491{
492 const char *line = readblineraw(fd, buf, bufsize, filesize, bread);
493
494 if(line)
495 cli_chomp(buf + (line - buf));
496
497 return line;
498}
499*/
500
501static unsigned int fmt_base64(char *dest, const char *src, unsigned int len)
502{
503 unsigned short bits = 0,temp = 0;
504 unsigned long written = 0;
505 unsigned int i;
506 const char base64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
507
508
509 for(i = 0; i < len; i++) {
510 temp <<= 8;
511 temp += src[i];
512 bits += 8;
513 while(bits > 6) {
514 dest[written] = base64[((temp >> (bits - 6)) & 63)];
515 written++;
516 bits -= 6;
517 }
518 }
519
520 if(bits) {
521 temp <<= (6 - bits);
522 dest[written] = base64[temp & 63];
523 written++;
524 }
525
526 while(written & 3) {
527 dest[written] = '=';
528 written++;
529 }
530
531 return written;
532}
533
534static char *proxyauth(const char *user, const char *pass)
535{
536 int len;
537 char *buf, *userpass, *auth;
538
539
540 userpass = malloc(strlen(user) + strlen(pass) + 2);
541 if(!userpass) {
542 logg("!proxyauth: Can't allocate memory for 'userpass'\n");
543 return NULL;
544 }
545 sprintf(userpass, "%s:%s", user, pass);
546
547 buf = malloc((strlen(pass) + strlen(user)) * 2 + 4);
548 if(!buf) {
549 logg("!proxyauth: Can't allocate memory for 'buf'\n");
550 free(userpass);
551 return NULL;
552 }
553
554 len = fmt_base64(buf, userpass, strlen(userpass));
555 free(userpass);
556 buf[len] = '\0';
557 auth = malloc(strlen(buf) + 30);
558 if(!auth) {
559 free(buf);
560 logg("!proxyauth: Can't allocate memory for 'authorization'\n");
561 return NULL;
562 }
563
564 sprintf(auth, "Proxy-Authorization: Basic %s\r\n", buf);
565 free(buf);
566
567 return auth;
568}
569
570#if BUILD_CLAMD
571int submitstats(const char *clamdcfg, const struct optstruct *opts)
572{
573 int sd, clamsockd, bread, cnt, ret;
574 char post[SUBMIT_MIN_ENTRIES * 256 + 512];
575 char query[SUBMIT_MIN_ENTRIES * 256];
576 char uastr[128], *line;
577 char *pt, *auth = NULL;
578 const char *country = NULL, *user, *proxy = NULL, *hostid = NULL;
579 const struct optstruct *opt;
580 unsigned int qcnt, entries, submitted = 0, permfail = 0, port = 0;
581 struct RCVLN rcv;
582 const char *tokens[5];
583
584
585 if((opt = optget(opts, "DetectionStatsCountry"))->enabled) {
586 if(strlen(opt->strarg) != 2 || !isalpha(opt->strarg[0]) || !isalpha(opt->strarg[1])) {
587 logg("!SubmitDetectionStats: DetectionStatsCountry requires a two-letter country code\n");
588 return 56;
589 }
590 country = opt->strarg;
591 }
592
593 if((opt = optget(opts, "DetectionStatsHostID"))->enabled) {
594 if(strlen(opt->strarg) != 32) {
595 logg("!SubmitDetectionStats: The unique ID must be 32 characters long\n");
596 return 56;
597 }
598 hostid = opt->strarg;
599 }
600
601 if((opt = optget(opts, "HTTPUserAgent"))->enabled)
602 strncpy(uastr, opt->strarg, sizeof(uastr));
603 else
604 snprintf(uastr, sizeof(uastr), PACKAGE"/%s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE"):%s:%s", get_version(), country ? country : "", hostid ? hostid : "");
605 uastr[sizeof(uastr) - 1] = 0;
606
607 if((opt = optget(opts, "HTTPProxyServer"))->enabled) {
608 proxy = opt->strarg;
609 if(!strncasecmp(proxy, "http://", 7))
610 proxy += 7;
611
612 if((opt = optget(opts, "HTTPProxyUsername"))->enabled) {
613 user = opt->strarg;
614 if(!(opt = optget(opts, "HTTPProxyPassword"))->enabled) {
615 logg("!SubmitDetectionStats: HTTPProxyUsername requires HTTPProxyPassword\n");
616 return 56;
617 }
618 auth = proxyauth(user, opt->strarg);
619 if(!auth)
620 return 56;
621 }
622
623 if((opt = optget(opts, "HTTPProxyPort"))->enabled)
624 port = opt->numarg;
625
626 logg("*Connecting via %s\n", proxy);
627 }
628
629 if((clamsockd = clamd_connect(clamdcfg, "SubmitDetectionStats")) < 0)
630 return 52;
631
632 recvlninit(&rcv, clamsockd);
633 if(sendln(clamsockd, "zDETSTATS", 10)) {
634 closesocket(clamsockd);
635 return 52;
636 }
637
638 ret = 0;
639 memset(query, 0, sizeof(query));
640 qcnt = 0;
641 entries = 0;
642 while(recvln(&rcv, &line, NULL) > 0) {
643 if(cli_strtokenize(line, ':', 5, tokens) != 5) {
644 logg("!SubmitDetectionStats: Invalid data format\n");
645 ret = 52;
646 break;
647 }
648
649 qcnt += snprintf(&query[qcnt], sizeof(query) - qcnt, "ts[]=%s&fname[]=%s&virus[]=%s(%s:%s)&", tokens[0], tokens[4], tokens[3], tokens[1], tokens[2]);
650 entries++;
651
652 if(entries == SUBMIT_MIN_ENTRIES) {
653 sd = wwwconnect("stats.clamav.net", proxy, port, NULL, optget(opts, "LocalIPAddress")->strarg, optget(opts, "ConnectTimeout")->numarg, NULL, 0, 0);
654 if(sd < 0) {
655 logg("!SubmitDetectionStats: Can't connect to server\n");
656 ret = 52;
657 break;
658 }
659 query[sizeof(query) - 1] = 0;
660 if(mdprintf(sd,
661 "POST http://stats.clamav.net/submit.php HTTP/1.0\r\n"
662 "Host: stats.clamav.net\r\n%s%s%s%s"
663 "Content-Type: application/x-www-form-urlencoded\r\n"
664 "User-Agent: %s\r\n"
665 "Content-Length: %u\r\n\r\n"
666 "%s",
667 auth ? auth : "", hostid ? "X-HostID: " : "", hostid ? hostid : "", hostid ? "\r\n" : "", uastr, (unsigned int) strlen(query), query) < 0)
668 {
669 logg("!SubmitDetectionStats: Can't write to socket\n");
670 ret = 52;
671 closesocket(sd);
672 break;
673 }
674
675 pt = post;
676 cnt = sizeof(post) - 1;
677#ifdef SO_ERROR
678 while((bread = wait_recv(sd, pt, cnt, 0, optget(opts, "ReceiveTimeout")->numarg)) > 0) {
679#else
680 while((bread = recv(sd, pt, cnt, 0)) > 0) {
681#endif
682 pt += bread;
683 cnt -= bread;
684 if(cnt <= 0)
685 break;
686 }
687 *pt = 0;
688 closesocket(sd);
689
690 if(bread < 0) {
691 logg("!SubmitDetectionStats: Can't read from socket\n");
692 ret = 52;
693 break;
694 }
695
696 if(strstr(post, "SUBMIT_OK")) {
697 submitted += entries;
698 if(submitted + SUBMIT_MIN_ENTRIES > SUBMIT_MAX_ENTRIES)
699 break;
700 qcnt = 0;
701 entries = 0;
702 memset(query, 0, sizeof(query));
703 continue;
704 }
705
706 ret = 52;
707 if((pt = strstr(post, "SUBMIT_PERMANENT_FAILURE"))) {
708 if(!submitted) {
709 permfail = 1;
710 if((pt + 32 <= post + sizeof(post)) && pt[24] == ':')
711 logg("!SubmitDetectionStats: Remote server reported permanent failure: %s\n", &pt[25]);
712 else
713 logg("!SubmitDetectionStats: Remote server reported permanent failure\n");
714 }
715 } else if((pt = strstr(post, "SUBMIT_TEMPORARY_FAILURE"))) {
716 if(!submitted) {
717 if((pt + 32 <= post + sizeof(post)) && pt[24] == ':')
718 logg("!SubmitDetectionStats: Remote server reported temporary failure: %s\n", &pt[25]);
719 else
720 logg("!SubmitDetectionStats: Remote server reported temporary failure\n");
721 }
722 } else {
723 if(!submitted)
724 logg("!SubmitDetectionStats: Incorrect answer from server\n");
725 }
726
727 break;
728 }
729 }
730 closesocket(clamsockd);
731 if(auth)
732 free(auth);
733
734 if(ret == 0) {
735 if(!submitted) {
736 logg("SubmitDetectionStats: Not enough recent data for submission\n");
737 } else {
738 logg("SubmitDetectionStats: Submitted %u records\n", submitted);
739 if((clamsockd = clamd_connect(clamdcfg, "SubmitDetectionStats")) != -1) {
740 sendln(clamsockd, "DETSTATSCLEAR", 14);
741 recv(clamsockd, query, sizeof(query), 0);
742 closesocket(clamsockd);
743 }
744 }
745 }
746 return ret;
747}
748#else
749int submitstats(const char *clamdcfg, const struct optstruct *opts)
750{
751 logg("clamd not built, no statistics");
752 return 52;
753}
754#endif
755
756static int Rfc2822DateTime(char *buf, time_t mtime)
757{
758 struct tm *gmt;
759
760 gmt = gmtime(&mtime);
761 if (!gmt) {
762 logg("gmtime: %s\n", strerror(errno));
763 strcpy(buf, "ERROR");
764 return -1;
765 }
766 return strftime(buf, 36, "%a, %d %b %Y %X GMT", gmt);
767}
768
769static struct cl_cvd *remote_cvdhead(const char *cvdfile, const char *localfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int *ims, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist)
770{
771 char cmd[512], head[513], buffer[FILEBUFF], ipaddr[46], *ch, *tmp;
772 int bread, cnt, sd;
773 unsigned int i, j;
774 char *remotename = NULL, *authorization = NULL;
775 struct cl_cvd *cvd;
776 char last_modified[36], uastr[128];
777
778
779 if(proxy) {
780 remotename = malloc(strlen(hostname) + 8);
781 if(!remotename) {
782 logg("!remote_cvdhead: Can't allocate memory for 'remotename'\n");
783 return NULL;
784 }
785 sprintf(remotename, "http://%s", hostname);
786
787 if(user) {
788 authorization = proxyauth(user, pass);
789 if(!authorization) {
790 free(remotename);
791 return NULL;
792 }
793 }
794 }
795
796 if(!access(localfile, R_OK)) {
797 cvd = cl_cvdhead(localfile);
798 if(!cvd) {
799 logg("!remote_cvdhead: Can't parse file %s\n", localfile);
800 free(remotename);
801 free(authorization);
802 return NULL;
803 }
804 Rfc2822DateTime(last_modified, (time_t) cvd->stime);
805 cl_cvdfree(cvd);
806 } else {
807 time_t mtime = 1104119530;
808
809 Rfc2822DateTime(last_modified, mtime);
810 logg("*Assuming modification time in the past\n");
811 }
812
813 logg("*If-Modified-Since: %s\n", last_modified);
814
815 logg("Reading CVD header (%s): ", cvdfile);
816
817 if(uas)
818 strncpy(uastr, uas, sizeof(uastr));
819 else
820 snprintf(uastr, sizeof(uastr), PACKAGE"/%s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")", get_version());
821 uastr[sizeof(uastr) - 1] = 0;
822
823 snprintf(cmd, sizeof(cmd),
824 "GET %s/%s HTTP/1.0\r\n"
825 "Host: %s\r\n%s"
826 "User-Agent: %s\r\n"
827 "Connection: close\r\n"
828 "Range: bytes=0-511\r\n"
829 "If-Modified-Since: %s\r\n"
830 "\r\n", (remotename != NULL) ? remotename : "", cvdfile, hostname, (authorization != NULL) ? authorization : "", uastr, last_modified);
831
832 free(remotename);
833 free(authorization);
834
835 memset(ipaddr, 0, sizeof(ipaddr));
836
837 if(ip[0]) /* use ip to connect */
838 sd = wwwconnect(ip, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist);
839 else
840 sd = wwwconnect(hostname, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist);
841
842 if(sd < 0) {
843 return NULL;
844 } else {
845 logg("*Connected to %s (IP: %s).\n", hostname, ipaddr);
846 logg("*Trying to retrieve CVD header of http://%s/%s\n", hostname, cvdfile);
847 }
848
849 if(!ip[0])
850 strcpy(ip, ipaddr);
851
852 if(send(sd, cmd, strlen(cmd), 0) < 0) {
853 logg("%cremote_cvdhead: write failed\n", logerr ? '!' : '^');
854 closesocket(sd);
855 return NULL;
856 }
857
858 tmp = buffer;
859 cnt = FILEBUFF;
860#ifdef SO_ERROR
861 while((bread = wait_recv(sd, tmp, cnt, 0, rtimeout)) > 0) {
862#else
863 while((bread = recv(sd, tmp, cnt, 0)) > 0) {
864#endif
865 tmp += bread;
866 cnt -= bread;
867 if(cnt <= 0)
868 break;
869 }
870 closesocket(sd);
871
872 if(bread == -1) {
873 logg("%cremote_cvdhead: Error while reading CVD header from %s\n", logerr ? '!' : '^', hostname);
874 mirman_update(mdat->currip, mdat->af, mdat, 1);
875 return NULL;
876 }
877
878 if((strstr(buffer, "HTTP/1.1 404")) != NULL || (strstr(buffer, "HTTP/1.0 404")) != NULL) {
879 logg("%cCVD file not found on remote server\n", logerr ? '!' : '^');
880 mirman_update(mdat->currip, mdat->af, mdat, 2);
881 return NULL;
882 }
883
884 /* check whether the resource is up-to-date */
885 if((strstr(buffer, "HTTP/1.1 304")) != NULL || (strstr(buffer, "HTTP/1.0 304")) != NULL) {
886 *ims = 0;
887 logg("OK (IMS)\n");
888 mirman_update(mdat->currip, mdat->af, mdat, 0);
889 return NULL;
890 } else {
891 *ims = 1;
892 }
893
894 if(!strstr(buffer, "HTTP/1.1 200") && !strstr(buffer, "HTTP/1.0 200") &&
895 !strstr(buffer, "HTTP/1.1 206") && !strstr(buffer, "HTTP/1.0 206")) {
896 logg("%cUnknown response from remote server\n", logerr ? '!' : '^');
897 mirman_update(mdat->currip, mdat->af, mdat, 1);
898 return NULL;
899 }
900
901 i = 3;
902 ch = buffer + i;
903 while(i < sizeof(buffer)) {
904 if (*ch == '\n' && *(ch - 1) == '\r' && *(ch - 2) == '\n' && *(ch - 3) == '\r') {
905 ch++;
906 i++;
907 break;
908 }
909 ch++;
910 i++;
911 }
912
913 if(sizeof(buffer) - i < 512) {
914 logg("%cremote_cvdhead: Malformed CVD header (too short)\n", logerr ? '!' : '^');
915 mirman_update(mdat->currip, mdat->af, mdat, 1);
916 return NULL;
917 }
918
919 memset(head, 0, sizeof(head));
920
921 for(j = 0; j < 512; j++) {
922 if(!ch || (ch && !*ch) || (ch && !isprint(ch[j]))) {
923 logg("%cremote_cvdhead: Malformed CVD header (bad chars)\n", logerr ? '!' : '^');
924 mirman_update(mdat->currip, mdat->af, mdat, 1);
925 return NULL;
926 }
927 head[j] = ch[j];
928 }
929
930 if(!(cvd = cl_cvdparse(head))) {
931 logg("%cremote_cvdhead: Malformed CVD header (can't parse)\n", logerr ? '!' : '^');
932 mirman_update(mdat->currip, mdat->af, mdat, 1);
933 } else {
934 logg("OK\n");
935 mirman_update(mdat->currip, mdat->af, mdat, 0);
936 }
937
938 return cvd;
939}
940
941static int getfile_mirman(const char *srcfile, const char *destfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist, const char *ims, const char *ipaddr, int sd)
942{
943 char cmd[512], uastr[128], buffer[FILEBUFF], *ch;
944 int bread, fd, totalsize = 0, rot = 0, totaldownloaded = 0,
945 percentage = 0;
946 unsigned int i;
947 char *remotename = NULL, *authorization = NULL, *headerline;
948 const char *rotation = "|/-\\", *fname;
949
950
951 if(proxy) {
952 remotename = malloc(strlen(hostname) + 8);
953 if(!remotename) {
954 logg("!getfile: Can't allocate memory for 'remotename'\n");
955 return 75; /* FIXME */
956 }
957 sprintf(remotename, "http://%s", hostname);
958
959 if(user) {
960 authorization = proxyauth(user, pass);
961 if(!authorization) {
962 free(remotename);
963 return 75; /* FIXME */
964 }
965 }
966 }
967
968 if(ims)
969 logg("*If-Modified-Since: %s\n", ims);
970
971 if(uas)
972 strncpy(uastr, uas, sizeof(uastr));
973 else
974 snprintf(uastr, sizeof(uastr), PACKAGE"/%s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")", get_version());
975 uastr[sizeof(uastr) - 1] = 0;
976
977 snprintf(cmd, sizeof(cmd),
978 "GET %s/%s HTTP/1.0\r\n"
979 "Host: %s\r\n%s"
980 "User-Agent: %s\r\n"
981#ifdef FRESHCLAM_NO_CACHE
982 "Cache-Control: no-cache\r\n"
983#endif
984 "Connection: close\r\n"
985 "%s%s%s"
986 "\r\n", (remotename != NULL) ? remotename : "", srcfile, hostname, (authorization != NULL) ? authorization : "", uastr, ims ? "If-Modified-Since: " : "", ims ? ims : "", ims ? "\r\n": "");
987
988 if(remotename)
989 free(remotename);
990
991 if(authorization)
992 free(authorization);
993
994 logg("*Trying to download http://%s/%s (IP: %s)\n", hostname, srcfile, ipaddr);
995
996 if(ip && !ip[0])
997 strcpy(ip, ipaddr);
998
999 if(send(sd, cmd, strlen(cmd), 0) < 0) {
1000 logg("%cgetfile: Can't write to socket\n", logerr ? '!' : '^');
1001 return 52;
1002 }
1003
1004 /* read http headers */
1005 ch = buffer;
1006 i = 0;
1007 while(1) {
1008 /* recv one byte at a time, until we reach \r\n\r\n */
1009#ifdef SO_ERROR
1010 if((i >= sizeof(buffer) - 1) || wait_recv(sd, buffer + i, 1, 0, rtimeout) == -1) {
1011#else
1012 if((i >= sizeof(buffer) - 1) || recv(sd, buffer + i, 1, 0) == -1) {
1013#endif
1014 logg("%cgetfile: Error while reading database from %s (IP: %s): %s\n", logerr ? '!' : '^', hostname, ipaddr, strerror(errno));
1015 if(mdat)
1016 mirman_update(mdat->currip, mdat->af, mdat, 1);
1017 return 52;
1018 }
1019
1020 if(i > 2 && *ch == '\n' && *(ch - 1) == '\r' && *(ch - 2) == '\n' && *(ch - 3) == '\r') {
1021 i++;
1022 break;
1023 }
1024 ch++;
1025 i++;
1026 }
1027
1028 buffer[i] = 0;
1029
1030 /* check whether the resource actually existed or not */
1031 if((strstr(buffer, "HTTP/1.1 404")) != NULL || (strstr(buffer, "HTTP/1.0 404")) != NULL) {
1032 logg("^getfile: %s not found on remote server (IP: %s)\n", srcfile, ipaddr);
1033 if(mdat)
1034 mirman_update(mdat->currip, mdat->af, mdat, 2);
1035 return 58;
1036 }
1037
1038 /* If-Modified-Since */
1039 if(strstr(buffer, "HTTP/1.1 304") || strstr(buffer, "HTTP/1.0 304")) {
1040 if(mdat)
1041 mirman_update(mdat->currip, mdat->af, mdat, 0);
1042 return 1;
1043 }
1044
1045 if(!strstr(buffer, "HTTP/1.1 200") && !strstr(buffer, "HTTP/1.0 200") &&
1046 !strstr(buffer, "HTTP/1.1 206") && !strstr(buffer, "HTTP/1.0 206")) {
1047 logg("%cgetfile: Unknown response from remote server (IP: %s)\n", logerr ? '!' : '^', ipaddr);
1048 if(mdat)
1049 mirman_update(mdat->currip, mdat->af, mdat, 1);
1050 return 58;
1051 }
1052
1053 /* get size of resource */
1054 for(i = 0; (headerline = cli_strtok(buffer, i, "\n")); i++){
1055 if(strstr(headerline, "Content-Length:")) {
1056 if((ch = cli_strtok(headerline, 1, ": "))) {
1057 totalsize = atoi(ch);
1058 free(ch);
1059 } else {
1060 totalsize = 0;
1061 }
1062 }
1063 free(headerline);
1064 }
1065
1066 if((fd = open(destfile, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644)) == -1) {
1067 char currdir[512];
1068
1069 if(getcwd(currdir, sizeof(currdir)))
1070 logg("!getfile: Can't create new file %s in %s\n", destfile, currdir);
1071 else
1072 logg("!getfile: Can't create new file %s in the current directory\n", destfile);
1073
1074 logg("Hint: The database directory must be writable for UID %d or GID %d\n", getuid(), getgid());
1075 return 57;
1076 }
1077
1078 if((fname = strrchr(srcfile, '/')))
1079 fname++;
1080 else
1081 fname = srcfile;
1082
1083#ifdef SO_ERROR
1084 while((bread = wait_recv(sd, buffer, FILEBUFF, 0, rtimeout)) > 0) {
1085#else
1086 while((bread = recv(sd, buffer, FILEBUFF, 0)) > 0) {
1087#endif
1088 if(write(fd, buffer, bread) != bread) {
1089 logg("getfile: Can't write %d bytes to %s\n", bread, destfile);
1090 close(fd);
1091 unlink(destfile);
1092 return 57; /* FIXME */
1093 }
1094
1095 totaldownloaded += bread;
1096 if(totalsize > 0)
1097 percentage = (int) (100 * (float) totaldownloaded / totalsize);
1098
1099 if(!mprintf_quiet) {
1100 if(totalsize > 0) {
1101 mprintf("Downloading %s [%3i%%]\r", fname, percentage);
1102 } else {
1103 mprintf("Downloading %s [%c]\r", fname, rotation[rot]);
1104 rot++;
1105 rot %= 4;
1106 }
1107 fflush(stdout);
1108 }
1109 }
1110 close(fd);
1111
1112 if(bread == -1) {
1113 logg("%cgetfile: Download interrupted: %s (IP: %s)\n", logerr ? '!' : '^', strerror(errno), ipaddr);
1114 if(mdat)
1115 mirman_update(mdat->currip, mdat->af, mdat, 2);
1116 return 52;
1117 }
1118
1119 if(!totaldownloaded)
1120 return 53;
1121
1122 if(totalsize > 0)
1123 logg("Downloading %s [100%%]\n", fname);
1124 else
1125 logg("Downloading %s [*]\n", fname);
1126
1127 if(mdat)
1128 mirman_update(mdat->currip, mdat->af, mdat, 0);
1129 return 0;
1130}
1131
1132static int getfile(const char *srcfile, const char *destfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist, const char *ims, const struct optstruct *opts)
1133{
1134 int ret, sd;
1135 char ipaddr[46];
1136
1137 memset(ipaddr, 0, sizeof(ipaddr));
1138 if(ip && ip[0]) /* use ip to connect */
1139 sd = wwwconnect(ip, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist);
1140 else
1141 sd = wwwconnect(hostname, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist);
1142
1143 if(sd < 0)
1144 return 52;
1145
1146 if(mdat) {
1147 mirman_update_sf(mdat->currip, mdat->af, mdat, 0, 1);
1148 mirman_write("mirrors.dat", dbdir, mdat);
1149 }
1150
1151 ret = getfile_mirman(srcfile, destfile, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, logerr, can_whitelist, ims, ipaddr, sd);
1152 closesocket(sd);
1153
1154 if(mdat) {
1155 mirman_update_sf(mdat->currip, mdat->af, mdat, 0, -1);
1156 mirman_write("mirrors.dat", dbdir, mdat);
1157 }
1158
1159 return ret;
1160}
1161
1162static int getcvd(const char *cvdfile, const char *newfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, unsigned int newver, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist, const struct optstruct *opts)
1163{
1164 struct cl_cvd *cvd;
1165 int ret;
1166
1167
1168 logg("*Retrieving http://%s/%s\n", hostname, cvdfile);
1169
1170 if((ret = getfile(cvdfile, newfile, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, logerr, can_whitelist, NULL, opts))) {
1171 logg("%cCan't download %s from %s\n", logerr ? '!' : '^', cvdfile, hostname);
1172 unlink(newfile);
1173 return ret;
1174 }
1175
1176 if((ret = cl_cvdverify(newfile))) {
1177 logg("!Verification: %s\n", cl_strerror(ret));
1178 unlink(newfile);
1179 return 54;
1180 }
1181
1182 if(!(cvd = cl_cvdhead(newfile))) {
1183 logg("!Can't read CVD header of new %s database.\n", cvdfile);
1184 unlink(newfile);
1185 return 54;
1186 }
1187
1188 if(cvd->version < newver) {
1189 logg("^Mirror %s is not synchronized.\n", ip);
1190 mirman_update(mdat->currip, mdat->af, mdat, 2);
1191 cl_cvdfree(cvd);
1192 unlink(newfile);
1193 return 59;
1194 }
1195
1196 cl_cvdfree(cvd);
1197 return 0;
1198}
1199
1200static int chdir_tmp(const char *dbname, const char *tmpdir)
1201{
1202 char cvdfile[32];
1203
1204
1205 if(access(tmpdir, R_OK|W_OK) == -1) {
1206 sprintf(cvdfile, "%s.cvd", dbname);
1207 if(access(cvdfile, R_OK) == -1) {
1208 sprintf(cvdfile, "%s.cld", dbname);
1209 if(access(cvdfile, R_OK) == -1) {
1210 logg("!chdir_tmp: Can't access local %s database\n", dbname);
1211 return -1;
1212 }
1213 }
1214
1215 if(mkdir(tmpdir, 0755) == -1) {
1216 logg("!chdir_tmp: Can't create directory %s\n", tmpdir);
1217 return -1;
1218 }
1219
1220 if(cli_cvdunpack(cvdfile, tmpdir) == -1) {
1221 logg("!chdir_tmp: Can't unpack %s into %s\n", cvdfile, tmpdir);
1222 cli_rmdirs(tmpdir);
1223 return -1;
1224 }
1225 }
1226
1227 if(chdir(tmpdir) == -1) {
1228 logg("!chdir_tmp: Can't change directory to %s\n", tmpdir);
1229 return -1;
1230 }
1231
1232 return 0;
1233}
1234
1235static int getpatch(const char *dbname, const char *tmpdir, int version, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist, const struct optstruct *opts)
1236{
1237 char *tempname, patch[32], olddir[512];
1238 int ret, fd;
1239
1240
1241 if(!getcwd(olddir, sizeof(olddir))) {
1242 logg("!getpatch: Can't get path of current working directory\n");
1243 return 50; /* FIXME */
1244 }
1245
1246 if(chdir_tmp(dbname, tmpdir) == -1)
1247 return 50;
1248
1249 tempname = cli_gentemp(".");
1250 snprintf(patch, sizeof(patch), "%s-%d.cdiff", dbname, version);
1251
1252 logg("*Retrieving http://%s/%s\n", hostname, patch);
1253 if((ret = getfile(patch, tempname, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, logerr, can_whitelist, NULL, opts))) {
1254 if(ret == 53)
1255 logg("Empty script %s, need to download entire database\n", patch);
1256 else
1257 logg("%cgetpatch: Can't download %s from %s\n", logerr ? '!' : '^', patch, hostname);
1258 unlink(tempname);
1259 free(tempname);
1260 CHDIR_ERR(olddir);
1261 return ret;
1262 }
1263
1264 if((fd = open(tempname, O_RDONLY|O_BINARY)) == -1) {
1265 logg("!getpatch: Can't open %s for reading\n", tempname);
1266 unlink(tempname);
1267 free(tempname);
1268 CHDIR_ERR(olddir);
1269 return 55;
1270 }
1271
1272 if(cdiff_apply(fd, 1) == -1) {
1273 logg("!getpatch: Can't apply patch\n");
1274 close(fd);
1275 unlink(tempname);
1276 free(tempname);
1277 CHDIR_ERR(olddir);
1278 return 70; /* FIXME */
1279 }
1280
1281 close(fd);
1282 unlink(tempname);
1283 free(tempname);
1284 if(chdir(olddir) == -1) {
1285 logg("!getpatch: Can't chdir to %s\n", olddir);
1286 return 50; /* FIXME */
1287 }
1288 return 0;
1289}
1290
1291static struct cl_cvd *currentdb(const char *dbname, char *localname)
1292{
1293 char db[32];
1294 struct cl_cvd *cvd = NULL;
1295
1296
1297 snprintf(db, sizeof(db), "%s.cvd", dbname);
1298 if(localname)
1299 strcpy(localname, db);
1300
1301 if(access(db, R_OK) == -1) {
1302 snprintf(db, sizeof(db), "%s.cld", dbname);
1303 if(localname)
1304 strcpy(localname, db);
1305 }
1306
1307 if(!access(db, R_OK))
1308 cvd = cl_cvdhead(db);
1309
1310 return cvd;
1311}
1312
1313static int buildcld(const char *tmpdir, const char *dbname, const char *newfile, unsigned int compr)
1314{
1315 DIR *dir;
1316 char cwd[512], info[32], buff[513], *pt;
1317 struct dirent *dent;
1318 int fd, err = 0;
1319 gzFile *gzs = NULL;
1320
1321 if(!getcwd(cwd, sizeof(cwd))) {
1322 logg("!buildcld: Can't get path of current working directory\n");
1323 return -1;
1324 }
1325
1326 if(chdir(tmpdir) == -1) {
1327 logg("!buildcld: Can't access directory %s\n", tmpdir);
1328 return -1;
1329 }
1330
1331 snprintf(info, sizeof(info), "%s.info", dbname);
1332 if((fd = open(info, O_RDONLY|O_BINARY)) == -1) {
1333 logg("!buildcld: Can't open %s\n", info);
1334 CHDIR_ERR(cwd);
1335 return -1;
1336 }
1337
1338 if(read(fd, buff, 512) == -1) {
1339 logg("!buildcld: Can't read %s\n", info);
1340 CHDIR_ERR(cwd);
1341 close(fd);
1342 return -1;
1343 }
1344 buff[512] = 0;
1345 close(fd);
1346
1347 if(!(pt = strchr(buff, '\n'))) {
1348 logg("!buildcld: Bad format of %s\n", info);
1349 CHDIR_ERR(cwd);
1350 return -1;
1351 }
1352 memset(pt, ' ', 512 + buff - pt);
1353
1354 if((fd = open(newfile, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644)) == -1) {
1355 logg("!buildcld: Can't open %s for writing\n", newfile);
1356 CHDIR_ERR(cwd);
1357 return -1;
1358 }
1359 if(write(fd, buff, 512) != 512) {
1360 logg("!buildcld: Can't write to %s\n", newfile);
1361 CHDIR_ERR(cwd);
1362 close(fd);
1363 unlink(newfile);
1364 return -1;
1365 }
1366
1367 if((dir = opendir(".")) == NULL) {
1368 logg("!buildcld: Can't open directory %s\n", tmpdir);
1369 CHDIR_ERR(cwd);
1370 close(fd);
1371 unlink(newfile);
1372 return -1;
1373 }
1374
1375 if(compr) {
1376 close(fd);
1377 if(!(gzs = gzopen(newfile, "ab9f"))) {
1378 logg("!buildcld: gzopen() failed for %s\n", newfile);
1379 CHDIR_ERR(cwd);
1380 closedir(dir);
1381 unlink(newfile);
1382 return -1;
1383 }
1384 }
1385
1386 if(access("COPYING", R_OK)) {
1387 logg("!buildcld: COPYING file not found\n");
1388 err = 1;
1389 } else {
1390 if(tar_addfile(fd, gzs, "COPYING") == -1) {
1391 logg("!buildcld: Can't add COPYING to .cld file\n");
1392 err = 1;
1393 }
1394 }
1395
1396 if(!err && !access(info, R_OK)) {
1397 if(tar_addfile(fd, gzs, info) == -1) {
1398 logg("!buildcld: Can't add %s to .cld file\n", info);
1399 err = 1;
1400 }
1401 }
1402
1403 if(!err && !access("daily.cfg", R_OK)) {
1404 if(tar_addfile(fd, gzs, "daily.cfg") == -1) {
1405 logg("!buildcld: Can't add daily.cfg to .cld file\n");
1406 err = 1;
1407 }
1408 }
1409
1410 if(err) {
1411 CHDIR_ERR(cwd);
1412 if(gzs)
1413 gzclose(gzs);
1414 else
1415 close(fd);
1416 closedir(dir);
1417 unlink(newfile);
1418 return -1;
1419 }
1420
1421 while((dent = readdir(dir))) {
1422 if(dent->d_ino)
1423 {
1424 if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..") || !strcmp(dent->d_name, "COPYING") || !strcmp(dent->d_name, "daily.cfg") || !strcmp(dent->d_name, info))
1425 continue;
1426
1427 if(tar_addfile(fd, gzs, dent->d_name) == -1) {
1428 logg("!buildcld: Can't add %s to .cld file\n", dent->d_name);
1429 CHDIR_ERR(cwd);
1430 if(gzs)
1431 gzclose(gzs);
1432 else
1433 close(fd);
1434 closedir(dir);
1435 unlink(newfile);
1436 return -1;
1437 }
1438 }
1439 }
1440 closedir(dir);
1441
1442 if(gzs) {
1443 if(gzclose(gzs)) {
1444 logg("!buildcld: gzclose() failed for %s\n", newfile);
1445 unlink(newfile);
1446 return -1;
1447 }
1448 } else {
1449 if(close(fd) == -1) {
1450 logg("!buildcld: close() failed for %s\n", newfile);
1451 unlink(newfile);
1452 return -1;
1453 }
1454 }
1455
1456 if(chdir(cwd) == -1) {
1457 logg("!buildcld: Can't return to previous directory %s\n", cwd);
1458 return -1;
1459 }
1460
1461 return 0;
1462}
1463
1464static int test_database(const char *newfile, const char *newdb, int bytecode)
1465{
1466 struct cl_engine *engine;
1467 unsigned newsigs = 0;
1468 int ret;
1469
1470 logg("*Loading signatures from %s\n", newdb);
1471 if(!(engine = cl_engine_new())) {
1472 return 55;
1473 }
1474
1475 if((ret = cl_load(newfile, engine, &newsigs, CL_DB_PHISHING | CL_DB_PHISHING_URLS | CL_DB_BYTECODE | CL_DB_PUA)) != CL_SUCCESS) {
1476 logg("!Failed to load new database: %s\n", cl_strerror(ret));
1477 cl_engine_free(engine);
1478 return 55;
1479 }
1480 if(bytecode && (ret = cli_bytecode_prepare2(engine, &engine->bcs, engine->dconf->bytecode/*FIXME: dconf has no sense here*/))) {
1481 logg("!Failed to compile/load bytecode: %s\n", cl_strerror(ret));
1482 cl_engine_free(engine);
1483 return 55;
1484 }
1485 logg("*Properly loaded %u signatures from new %s\n", newsigs, newdb);
1486 if(engine->domainlist_matcher && engine->domainlist_matcher->sha256_pfx_set.keys)
1487 cli_hashset_destroy(&engine->domainlist_matcher->sha256_pfx_set);
1488 cl_engine_free(engine);
1489 return 0;
1490}
1491
1492#ifndef WIN32
1493static int test_database_wrap(const char *file, const char *newdb, int bytecode)
1494{
1495 char firstline[256];
1496 char lastline[256];
1497 int pipefd[2];
1498 pid_t pid;
1499 int status = 0, ret;
1500 FILE *f;
1501
1502 if (pipe(pipefd) == -1) {
1503 logg("^pipe() failed: %s\n", strerror(errno));
1504 return test_database(file, newdb, bytecode);
1505 }
1506
1507 switch ( pid = fork() ) {
1508 case 0:
1509 close(pipefd[0]);
1510 dup2(pipefd[1], 2);
1511 exit(test_database(file, newdb, bytecode));
1512 case -1:
1513 close(pipefd[0]);
1514 close(pipefd[1]);
1515 logg("^fork() failed: %s\n", strerror(errno));
1516 return test_database(file, newdb, bytecode);
1517 default:
1518 /* read first / last line printed by child*/
1519 close(pipefd[1]);
1520 f = fdopen(pipefd[0], "r");
1521 firstline[0] = 0;
1522 lastline[0] = 0;
1523 do {
1524 if (!fgets(firstline, sizeof(firstline), f))
1525 break;
1526 /* ignore warning messages, otherwise the outdated warning will
1527 * make us miss the important part of the error message */
1528 } while (!strncmp(firstline, "LibClamAV Warning:", 18));
1529 /* must read entire output, child doesn't like EPIPE */
1530 while (fgets(lastline, sizeof(firstline), f)) {
1531 /* print the full output only when LogVerbose or -v is given */
1532 logg("*%s", lastline);
1533 }
1534 fclose(f);
1535
1536 while ((ret = waitpid(pid, &status, 0)) == -1 && errno == EINTR);
1537 if (ret == -1 && errno != ECHILD)
1538 logg("^waitpid() failed: %s\n", strerror(errno));
1539 cli_chomp(firstline);
1540 cli_chomp(lastline);
1541 if (firstline[0]) {
1542 logg("!During database load : %s%s%s\n",
1543 firstline, lastline[0] ? " [...] " : "",
1544 lastline);
1545 }
1546 if (WIFEXITED(status)) {
1547 ret = WEXITSTATUS(status);
1548 if (ret) {
1549 logg("^Database load exited with status %d\n", ret);
1550 return ret;
1551 }
1552 if (firstline[0])
1553 logg("^Database successfully loaded, but there is stderr output\n");
1554 return 0;
1555 }
1556 if (WIFSIGNALED(status)) {
1557 logg("!Database load killed by signal %d\n", WTERMSIG(status));
1558 return 55;
1559 }
1560 logg("^Unknown status from wait: %d\n", status);
1561 return 55;
1562 }
1563}
1564#else
1565static int test_database_wrap(const char *file, const char *newdb, int bytecode)
1566{
1567 int ret = 55;
1568 __try
1569 {
1570 ret = test_database(file, newdb, bytecode);
1571 }
1572 __except (logg("!Exception during database testing, code %08x\n",
1573 GetExceptionCode()),
1574 EXCEPTION_CONTINUE_SEARCH)
1575 { }
1576 return ret;
1577}
1578#endif
1579
1580static int checkdbdir(void)
1581{
1582 DIR *dir;
1583 struct dirent *dent;
1584 char fname[512], broken[513];
1585 int ret, fret = 0;
1586
1587 if(!(dir = opendir(dbdir))) {
1588 logg("!checkdbdir: Can't open directory %s\n", dbdir);
1589 return -1;
1590 }
1591
1592 while((dent = readdir(dir))) {
1593 if(dent->d_ino) {
1594 if(cli_strbcasestr(dent->d_name, ".cld") || cli_strbcasestr(dent->d_name, ".cvd")) {
1595 snprintf(fname, sizeof(fname), "%s"PATHSEP"%s", dbdir, dent->d_name);
1596 if((ret = cl_cvdverify(fname))) {
1597 fret = -1;
1598 mprintf("!Corrupted database file %s: %s\n", fname, cl_strerror(ret));
1599 snprintf(broken, sizeof(broken), "%s.broken", fname);
1600 if(!access(broken, R_OK))
1601 unlink(broken);
1602 if(rename(fname, broken)) {
1603 if(unlink(fname))
1604 mprintf("!Can't remove broken database file %s, please delete it manually and restart freshclam\n", fname);
1605 } else {
1606 mprintf("Corrupted database file renamed to %s\n", broken);
1607 }
1608 }
1609 }
1610 }
1611 }
1612
1613 closedir(dir);
1614 return fret;
1615}
1616
1617extern int sigchld_wait;
1618
1619static int updatedb(const char *dbname, const char *hostname, char *ip, int *signo, const struct optstruct *opts, const char *dnsreply, char *localip, int outdated, struct mirdat *mdat, int logerr, int extra)
1620{
1621 struct cl_cvd *current, *remote;
1622 const struct optstruct *opt;
1623 unsigned int nodb = 0, currver = 0, newver = 0, port = 0, i, j;
1624 int ret, ims = -1;
1625 char *pt, cvdfile[32], localname[32], *tmpdir = NULL, *newfile, *newfile2, newdb[32];
1626 char extradbinfo[64], *extradnsreply = NULL, squery[64];
1627 const char *proxy = NULL, *user = NULL, *pass = NULL, *uas = NULL;
1628 unsigned int flevel = cl_retflevel(), remote_flevel = 0, maxattempts;
1629 unsigned int can_whitelist = 0, mirror_stats = 0;
1630#ifdef _WIN32
1631 unsigned int w32 = 1;
1632#else
1633 unsigned int w32 = 0;
1634#endif
1635 int ctimeout, rtimeout;
1636
1637
1638 if(cli_strbcasestr(hostname, ".clamav.net"))
1639 mirror_stats = 1;
1640
1641 snprintf(cvdfile, sizeof(cvdfile), "%s.cvd", dbname);
1642
1643 if(!(current = currentdb(dbname, localname))) {
1644 nodb = 1;
1645 } else {
1646 mdat->dbflevel = current->fl;
1647 }
1648
1649 if(!nodb && !extra && dnsreply) {
1650 int field = 0;
1651
1652 if(!strcmp(dbname, "main")) {
1653 field = 1;
1654 } else if(!strcmp(dbname, "daily")) {
1655 field = 2;
1656 } else if(!strcmp(dbname, "safebrowsing")) {
1657 field = 6;
1658 } else if(!strcmp(dbname, "bytecode")) {
1659 field = 7;
1660 } else {
1661 logg("!updatedb: Unknown database name (%s) passed.\n", dbname);
1662 cl_cvdfree(current);
1663 return 70;
1664 }
1665
1666 if(field && (pt = cli_strtok(dnsreply, field, ":"))) {
1667 if(!cli_isnumber(pt)) {
1668 logg("^Broken database version in TXT record.\n");
1669 } else {
1670 newver = atoi(pt);
1671 logg("*%s version from DNS: %d\n", cvdfile, newver);
1672 }
1673 free(pt);
1674 } else {
1675 logg("^Invalid DNS reply. Falling back to HTTP mode.\n");
1676 }
1677 }
1678#ifdef HAVE_RESOLV_H
1679 else if(!nodb && extra && !optget(opts, "no-dns")->enabled) {
1680 snprintf(extradbinfo, sizeof(extradbinfo), "%s.cvd.clamav.net", dbname);
1681 if((extradnsreply = dnsquery(extradbinfo, T_TXT, NULL))) {
1682 if((pt = cli_strtok(extradnsreply, 1, ":"))) {
1683 int rt;
1684 time_t ct;
1685
1686 rt = atoi(pt);
1687 free(pt);
1688 time(&ct);
1689 if((int) ct - rt > 10800) {
1690 logg("^DNS record is older than 3 hours.\n");
1691 free(extradnsreply);
1692 extradnsreply = NULL;
1693 }
1694 } else {
1695 logg("^No timestamp in TXT record for %s\n", cvdfile);
1696 free(extradnsreply);
1697 extradnsreply = NULL;
1698 }
1699 if((pt = cli_strtok(extradnsreply, 0, ":"))) {
1700 if(!cli_isnumber(pt)) {
1701 logg("^Broken database version in TXT record for %s\n", cvdfile);
1702 } else {
1703 newver = atoi(pt);
1704 logg("*%s version from DNS: %d\n", cvdfile, newver);
1705 }
1706 free(pt);
1707 } else {
1708 logg("^Invalid DNS reply. Falling back to HTTP mode.\n");
1709 }
1710 }
1711 }
1712#endif
1713
1714 if(dnsreply && !extra) {
1715 if((pt = cli_strtok(dnsreply, 5, ":"))) {
1716 remote_flevel = atoi(pt);
1717 free(pt);
1718 if(remote_flevel && (remote_flevel - flevel < 4))
1719 can_whitelist = 1;
1720 }
1721 }
1722
1723 /* Initialize proxy settings */
1724 if((opt = optget(opts, "HTTPProxyServer"))->enabled) {
1725 proxy = opt->strarg;
1726 if(strncasecmp(proxy, "http://", 7) == 0)
1727 proxy += 7;
1728
1729 if((opt = optget(opts, "HTTPProxyUsername"))->enabled) {
1730 user = opt->strarg;
1731 if((opt = optget(opts, "HTTPProxyPassword"))->enabled) {
1732 pass = opt->strarg;
1733 } else {
1734 logg("HTTPProxyUsername requires HTTPProxyPassword\n");
1735 if(current)
1736 cl_cvdfree(current);
1737 return 56;
1738 }
1739 }
1740
1741 if((opt = optget(opts, "HTTPProxyPort"))->enabled)
1742 port = opt->numarg;
1743
1744 logg("Connecting via %s\n", proxy);
1745 }
1746
1747 if((opt = optget(opts, "HTTPUserAgent"))->enabled)
1748 uas = opt->strarg;
1749
1750 ctimeout = optget(opts, "ConnectTimeout")->numarg;
1751 rtimeout = optget(opts, "ReceiveTimeout")->numarg;
1752
1753 if(!nodb && !newver) {
1754
1755 remote = remote_cvdhead(cvdfile, localname, hostname, ip, localip, proxy, port, user, pass, uas, &ims, ctimeout, rtimeout, mdat, logerr, can_whitelist);
1756
1757 if(!nodb && !ims) {
1758 logg("%s is up to date (version: %d, sigs: %d, f-level: %d, builder: %s)\n", localname, current->version, current->sigs, current->fl, current->builder);
1759 *signo += current->sigs;
1760 if(mirror_stats && strlen(ip)) {
1761 snprintf(squery, sizeof(squery), "%s.%u.%u.%u.%u.%s.ping.clamav.net", dbname, current->version, flevel, 1, w32, ip);
1762 dnsquery(squery, T_A, NULL);
1763 }
1764 cl_cvdfree(current);
1765 return 1;
1766 }
1767
1768 if(!remote) {
1769 logg("^Can't read %s header from %s (IP: %s)\n", cvdfile, hostname, ip);
1770 if(mirror_stats && strlen(ip)) {
1771 snprintf(squery, sizeof(squery), "%s.%u.%u.%u.%u.%s.ping.clamav.net", dbname, current->version + 1, flevel, 0, w32, ip);
1772 dnsquery(squery, T_A, NULL);
1773 }
1774 cl_cvdfree(current);
1775 return 58;
1776 }
1777
1778 newver = remote->version;
1779 cl_cvdfree(remote);
1780 }
1781
1782 if(!nodb && (current->version >= newver)) {
1783 logg("%s is up to date (version: %d, sigs: %d, f-level: %d, builder: %s)\n", localname, current->version, current->sigs, current->fl, current->builder);
1784
1785 if(!outdated && flevel < current->fl) {
1786 /* display warning even for already installed database */
1787 logg("^Current functionality level = %d, recommended = %d\n", flevel, current->fl);
1788 logg("Please check if ClamAV tools are linked against the proper version of libclamav\n");
1789 logg("DON'T PANIC! Read http://www.clamav.net/support/faq\n");
1790 }
1791
1792 *signo += current->sigs;
1793 cl_cvdfree(current);
1794 return 1;
1795 }
1796
1797 if(current) {
1798 currver = current->version;
1799 cl_cvdfree(current);
1800 }
1801
1802 /*
1803 if(ipaddr[0]) {
1804 hostfd = wwwconnect(ipaddr, proxy, port, NULL, localip);
1805 } else {
1806 hostfd = wwwconnect(hostname, proxy, port, ipaddr, localip);
1807 if(!ip[0])
1808 strcpy(ip, ipaddr);
1809 }
1810
1811 if(hostfd < 0) {
1812 if(ipaddr[0])
1813 logg("Connection with %s (IP: %s) failed.\n", hostname, ipaddr);
1814 else
1815 logg("Connection with %s failed.\n", hostname);
1816 return 52;
1817 };
1818 */
1819
1820 if(!optget(opts, "ScriptedUpdates")->enabled)
1821 nodb = 1;
1822
1823 newfile = cli_gentemp(updtmpdir);
1824 if(nodb) {
1825 ret = getcvd(cvdfile, newfile, hostname, ip, localip, proxy, port, user, pass, uas, newver, ctimeout, rtimeout, mdat, logerr, can_whitelist, opts);
1826 if(ret) {
1827 if(mirror_stats && strlen(ip)) {
1828 snprintf(squery, sizeof(squery), "%s.%u.%u.%u.%u.%s.ping.clamav.net", dbname, 0, flevel, 0, w32, ip);
1829 dnsquery(squery, T_A, NULL);
1830 }
1831 memset(ip, 0, 16);
1832 free(newfile);
1833 return ret;
1834 }
1835 snprintf(newdb, sizeof(newdb), "%s.cvd", dbname);
1836
1837 } else {
1838 ret = 0;
1839
1840 tmpdir = cli_gentemp(updtmpdir);
1841 maxattempts = optget(opts, "MaxAttempts")->numarg;
1842 for(i = currver + 1; i <= newver; i++) {
1843 for(j = 0; j < maxattempts; j++) {
1844 int llogerr = logerr;
1845 if(logerr)
1846 llogerr = (j == maxattempts - 1);
1847 ret = getpatch(dbname, tmpdir, i, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, llogerr, can_whitelist, opts);
1848 if(ret == 52 || ret == 58) {
1849 if(mirror_stats && strlen(ip)) {
1850 snprintf(squery, sizeof(squery), "%s.%u.%u.%u.%u.%s.ping.clamav.net", dbname, i, flevel, 0, w32, ip);
1851 dnsquery(squery, T_A, NULL);
1852 }
1853 memset(ip, 0, 16);
1854 continue;
1855 } else {
1856 break;
1857 }
1858 }
1859 if(ret)
1860 break;
1861 }
1862
1863 if(ret) {
1864 cli_rmdirs(tmpdir);
1865 free(tmpdir);
1866 if(ret != 53)
1867 logg("^Incremental update failed, trying to download %s\n", cvdfile);
1868 mirman_whitelist(mdat, 2);
1869 ret = getcvd(cvdfile, newfile, hostname, ip, localip, proxy, port, user, pass, uas, newver, ctimeout, rtimeout, mdat, logerr, can_whitelist, opts);
1870 if(ret) {
1871 if(mirror_stats && strlen(ip)) {
1872 snprintf(squery, sizeof(squery), "%s.%u.%u.%u.%u.%s.ping.clamav.net", dbname, 0, flevel, 0, w32, ip);
1873 dnsquery(squery, T_A, NULL);
1874 }
1875 free(newfile);
1876 return ret;
1877 }
1878 snprintf(newdb, sizeof(newdb), "%s.cvd", dbname);
1879 } else {
1880 if(buildcld(tmpdir, dbname, newfile, optget(opts, "CompressLocalDatabase")->enabled) == -1) {
1881 logg("!Can't create local database\n");
1882 cli_rmdirs(tmpdir);
1883 free(tmpdir);
1884 free(newfile);
1885 return 70; /* FIXME */
1886 }
1887 snprintf(newdb, sizeof(newdb), "%s.cld", dbname);
1888 cli_rmdirs(tmpdir);
1889 free(tmpdir);
1890 }
1891 }
1892
1893 if(!(current = cl_cvdhead(newfile))) {
1894 logg("!Can't parse new database %s\n", newfile);
1895 unlink(newfile);
1896 free(newfile);
1897 return 55; /* FIXME */
1898 }
1899
1900 if(optget(opts, "TestDatabases")->enabled && strlen(newfile) > 4) {
1901 newfile2 = strdup(newfile);
1902 if(!newfile2) {
1903 logg("!Can't allocate memory for filename!\n");
1904 unlink(newfile);
1905 free(newfile);
1906 return 55;
1907 }
1908 newfile2[strlen(newfile2) - 4] = '.';
1909 newfile2[strlen(newfile2) - 3] = 'c';
1910 newfile2[strlen(newfile2) - 2] = strstr(newdb, ".cld") ? 'l' : 'v';
1911 newfile2[strlen(newfile2) - 1] = 'd';
1912 if(rename(newfile, newfile2) == -1) {
1913 logg("!Can't rename %s to %s: %s\n", newfile, newfile2, strerror(errno));
1914 unlink(newfile);
1915 free(newfile);
1916 free(newfile2);
1917 return 57;
1918 }
1919 free(newfile);
1920 newfile = newfile2;
1921 sigchld_wait = 0;/* we need to wait() for the child ourselves */
1922 if (test_database_wrap(newfile, newdb, optget(opts, "Bytecode")->enabled)) {
1923 logg("!Failed to load new database: %s\n", cl_strerror(ret));
1924 unlink(newfile);
1925 free(newfile);
1926 return 55;
1927 }
1928 sigchld_wait = 1;
1929 }
1930
1931#if defined(_WIN32) || defined(C_OS2)
1932 if(!access(newdb, R_OK) && unlink(newdb)) {
1933 logg("!Can't unlink %s. Please fix the problem manually and try again.\n", newdb);
1934 unlink(newfile);
1935 free(newfile);
1936 cl_cvdfree(current);
1937 return 53;
1938 }
1939#endif
1940
1941 if(rename(newfile, newdb) == -1) {
1942 logg("!Can't rename %s to %s: %s\n", newfile, newdb, strerror(errno));
1943 unlink(newfile);
1944 free(newfile);
1945 cl_cvdfree(current);
1946 return 57;
1947 }
1948 free(newfile);
1949
1950 if(!nodb && !access(localname, R_OK) && strcmp(newdb, localname))
1951 if(unlink(localname))
1952 logg("^Can't unlink the old database file %s. Please remove it manually.\n", localname);
1953
1954 if(!optget(opts, "ScriptedUpdates")->enabled) {
1955 snprintf(localname, sizeof(localname), "%s.cld", dbname);
1956 if(!access(localname, R_OK))
1957 if(unlink(localname))
1958 logg("^Can't unlink the old database file %s. Please remove it manually.\n", localname);
1959 }
1960
1961 logg("%s updated (version: %d, sigs: %d, f-level: %d, builder: %s)\n", newdb, current->version, current->sigs, current->fl, current->builder);
1962
1963 if(flevel < current->fl) {
1964 logg("^Your ClamAV installation is OUTDATED!\n");
1965 logg("^Current functionality level = %d, recommended = %d\n", flevel, current->fl);
1966 logg("DON'T PANIC! Read http://www.clamav.net/support/faq\n");
1967 }
1968
1969 *signo += current->sigs;
1970 if(mirror_stats && strlen(ip)) {
1971 snprintf(squery, sizeof(squery), "%s.%u.%u.%u.%u.%s.ping.clamav.net", dbname, current->version, flevel, 1, w32, ip);
1972 dnsquery(squery, T_A, NULL);
1973 }
1974 cl_cvdfree(current);
1975 return 0;
1976}
1977
1978static int updatecustomdb(const char *url, int *signo, const struct optstruct *opts, char *localip, int logerr)
1979{
1980 const struct optstruct *opt;
1981 unsigned int port = 0, sigs = 0;
1982 int ret;
1983 char *pt, *host, urlcpy[256], *newfile = NULL, mtime[36], *newfile2;
1984 const char *proxy = NULL, *user = NULL, *pass = NULL, *uas = NULL, *rpath, *dbname;
1985 int ctimeout, rtimeout;
1986 struct stat sb;
1987 struct cl_cvd *cvd;
1988
1989 if(!strncasecmp(url, "http://", 7)) {
1990 strncpy(urlcpy, url, sizeof(urlcpy));
1991 host = &urlcpy[7];
1992 if(!(pt = strchr(host, '/'))) {
1993 logg("!DatabaseCustomURL: Incorrect URL\n");
1994 return 70;
1995 }
1996 *pt = 0;
1997 rpath = &url[pt - urlcpy + 1];
1998 dbname = strrchr(url, '/') + 1;
1999 if(!dbname || strlen(dbname) < 4) {
2000 logg("DatabaseCustomURL: Incorrect URL\n");
2001 return 70;
2002 }
2003
2004 /* Initialize proxy settings */
2005 if((opt = optget(opts, "HTTPProxyServer"))->enabled) {
2006 proxy = opt->strarg;
2007 if(strncasecmp(proxy, "http://", 7) == 0)
2008 proxy += 7;
2009
2010 if((opt = optget(opts, "HTTPProxyUsername"))->enabled) {
2011 user = opt->strarg;
2012 if((opt = optget(opts, "HTTPProxyPassword"))->enabled) {
2013 pass = opt->strarg;
2014 } else {
2015 logg("HTTPProxyUsername requires HTTPProxyPassword\n");
2016 return 56;
2017 }
2018 }
2019 if((opt = optget(opts, "HTTPProxyPort"))->enabled)
2020 port = opt->numarg;
2021 logg("Connecting via %s\n", proxy);
2022 }
2023
2024 if((opt = optget(opts, "HTTPUserAgent"))->enabled)
2025 uas = opt->strarg;
2026
2027 ctimeout = optget(opts, "ConnectTimeout")->numarg;
2028 rtimeout = optget(opts, "ReceiveTimeout")->numarg;
2029
2030 *mtime = 0;
2031 if(stat(dbname, &sb) != -1)
2032 Rfc2822DateTime(mtime, sb.st_mtime);
2033
2034 newfile = cli_gentemp(updtmpdir);
2035 ret = getfile(rpath, newfile, host, NULL, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, NULL, logerr, 0, *mtime ? mtime : NULL, opts);
2036 if(ret == 1) {
2037 logg("%s is up to date (version: custom database)\n", dbname);
2038 unlink(newfile);
2039 free(newfile);
2040 return 1;
2041 } else if(ret > 1) {
2042 logg("%cCan't download %s from %s\n", logerr ? '!' : '^', dbname, host);
2043 unlink(newfile);
2044 free(newfile);
2045 return ret;
2046 }
2047
2048 } else if(!strncasecmp(url, "file://", 7)) {
2049 rpath = &url[7];
2050#ifdef _WIN32
2051 dbname = strrchr(rpath, '\\');
2052#else
2053 dbname = strrchr(rpath, '/');
2054#endif
2055 if(!dbname || strlen(dbname++) < 5) {
2056 logg("DatabaseCustomURL: Incorrect URL\n");
2057 return 70;
2058 }
2059
2060 newfile = cli_gentemp(updtmpdir);
2061 if(!newfile)
2062 return 70;
2063
2064 /* FIXME: preserve file permissions, calculate % */
2065 logg("Downloading %s [ 0%%]\r", dbname);
2066 if(cli_filecopy(rpath, newfile) == -1) {
2067 logg("DatabaseCustomURL: Can't copy file %s into database directory\n", rpath);
2068 free(newfile);
2069 return 70;
2070 }
2071 logg("Downloading %s [100%%]\n", dbname);
2072 } else {
2073 logg("!DatabaseCustomURL: Not supported protocol\n");
2074 return 70;
2075 }
2076
2077 if(optget(opts, "TestDatabases")->enabled && strlen(newfile) > 4) {
2078 newfile2 = malloc(strlen(newfile) + strlen(dbname) + 1);
2079 if(!newfile2) {
2080 unlink(newfile);
2081 free(newfile);
2082 return 55;
2083 }
2084 sprintf(newfile2, "%s%s", newfile, dbname);
2085 newfile2[strlen(newfile) + strlen(dbname)] = 0;
2086 if(rename(newfile, newfile2) == -1) {
2087 logg("!Can't rename %s to %s: %s\n", newfile, newfile2, strerror(errno));
2088 unlink(newfile);
2089 free(newfile);
2090 free(newfile2);
2091 return 57;
2092 }
2093 free(newfile);
2094 newfile = newfile2;
2095 sigchld_wait = 0;/* we need to wait() for the child ourselves */
2096 if (test_database_wrap(newfile, dbname, optget(opts, "Bytecode")->enabled)) {
2097 logg("!Failed to load new database: %s\n", cl_strerror(ret));
2098 unlink(newfile);
2099 free(newfile);
2100 return 55;
2101 }
2102 sigchld_wait = 1;
2103 }
2104
2105#ifdef _WIN32
2106 if(!access(dbname, R_OK) && unlink(dbname)) {
2107 logg("!Can't unlink %s. Please fix the problem manually and try again.\n", dbname);
2108 unlink(newfile);
2109 free(newfile);
2110 return 53;
2111 }
2112#endif
2113
2114 if(rename(newfile, dbname) == -1) {
2115 logg("!Can't rename %s to %s: %s\n", newfile, dbname, strerror(errno));
2116 unlink(newfile);
2117 free(newfile);
2118 return 57;
2119 }
2120 free(newfile);
2121
2122 if(cli_strbcasestr(dbname, ".cld") || cli_strbcasestr(dbname, ".cvd")) {
2123 if((cvd = cl_cvdhead(dbname))) {
2124 sigs = cvd->sigs;
2125 cl_cvdfree(cvd);
2126 }
2127 } else if(cli_strbcasestr(dbname, ".cbc")) {
2128 sigs = 1;
2129 } else {
2130 sigs = countlines(dbname);
2131 }
2132
2133 logg("%s updated (version: custom database, sigs: %u)\n", dbname, sigs);
2134 *signo += sigs;
2135 return 0;
2136}
2137
2138int downloadmanager(const struct optstruct *opts, const char *hostname, int logerr)
2139{
2140 time_t currtime;
2141 int ret, updated = 0, outdated = 0, signo = 0;
2142 unsigned int ttl;
2143 char ipaddr[46], *dnsreply = NULL, *pt, *localip = NULL, *newver = NULL;
2144 const struct optstruct *opt;
2145 struct mirdat mdat;
2146#ifdef HAVE_RESOLV_H
2147 const char *dnsdbinfo;
2148#endif
2149
2150 pt = cli_gentemp(dbdir);
2151 if(!pt)
2152 return 57;
2153 strncpy(updtmpdir, pt, sizeof(updtmpdir));
2154 free(pt);
2155 if(mkdir(updtmpdir, 0755)) {
2156 logg("!Can't create temporary directory %s\n", updtmpdir);
2157 logg("Hint: The database directory must be writable for UID %d or GID %d\n", getuid(), getgid());
2158 return 57;
2159 }
2160
2161 time(&currtime);
2162 logg("ClamAV update process started at %s", ctime(&currtime));
2163#ifdef HAVE_GETADDRINFO
2164 logg("*Using IPv6 aware code\n");
2165#endif
2166
2167#ifdef HAVE_RESOLV_H
2168 dnsdbinfo = optget(opts, "DNSDatabaseInfo")->strarg;
2169
2170 if(optget(opts, "no-dns")->enabled) {
2171 dnsreply = NULL;
2172 } else {
2173 if((dnsreply = dnsquery(dnsdbinfo, T_TXT, &ttl))) {
2174 logg("*TTL: %d\n", ttl);
2175
2176 if((pt = cli_strtok(dnsreply, 3, ":"))) {
2177 int rt;
2178 time_t ct;
2179
2180 rt = atoi(pt);
2181 free(pt);
2182 time(&ct);
2183 if((int) ct - rt > 10800) {
2184 logg("^DNS record is older than 3 hours.\n");
2185 free(dnsreply);
2186 dnsreply = NULL;
2187 }
2188
2189 } else {
2190 free(dnsreply);
2191 dnsreply = NULL;
2192 }
2193
2194 if(dnsreply) {
2195 int vwarning = 1;
2196
2197 if((pt = cli_strtok(dnsreply, 4, ":"))) {
2198 if(*pt == '0')
2199 vwarning = 0;
2200
2201 free(pt);
2202 }
2203
2204 if((newver = cli_strtok(dnsreply, 0, ":"))) {
2205 char vstr[32];
2206
2207 logg("*Software version from DNS: %s\n", newver);
2208 strncpy(vstr, get_version(), 32);
2209 vstr[31] = 0;
2210 if(vwarning && !strstr(vstr, "devel") && !strstr(vstr, "rc")) {
2211 pt = strchr(vstr, '-');
2212 if((pt && strncmp(vstr, newver, pt - vstr)) || (!pt && strcmp(vstr, newver))) {
2213 logg("^Your ClamAV installation is OUTDATED!\n");
2214 logg("^Local version: %s Recommended version: %s\n", vstr, newver);
2215 logg("DON'T PANIC! Read http://www.clamav.net/support/faq\n");
2216 outdated = 1;
2217 }
2218 }
2219 }
2220 }
2221 }
2222
2223 if(!dnsreply) {
2224 logg("^Invalid DNS reply. Falling back to HTTP mode.\n");
2225 }
2226 }
2227#endif /* HAVE_RESOLV_H */
2228
2229 if((opt = optget(opts, "LocalIPAddress"))->enabled)
2230 localip = opt->strarg;
2231
2232 if(optget(opts, "HTTPProxyServer")->enabled)
2233 mirman_read("mirrors.dat", &mdat, 0);
2234 else
2235 mirman_read("mirrors.dat", &mdat, 1);
2236
2237 memset(ipaddr, 0, sizeof(ipaddr));
2238
2239 if((ret = updatedb("main", hostname, ipaddr, &signo, opts, dnsreply, localip, outdated, &mdat, logerr, 0)) > 50) {
2240 if(dnsreply)
2241 free(dnsreply);
2242
2243 if(newver)
2244 free(newver);
2245
2246 mirman_write("mirrors.dat", dbdir, &mdat);
2247 mirman_free(&mdat);
2248 cli_rmdirs(updtmpdir);
2249 return ret;
2250
2251 } else if(ret == 0)
2252 updated = 1;
2253
2254 /* if ipaddr[0] != 0 it will use it to connect to the web host */
2255 if((ret = updatedb("daily", hostname, ipaddr, &signo, opts, dnsreply, localip, outdated, &mdat, logerr, 0)) > 50) {
2256 if(dnsreply)
2257 free(dnsreply);
2258
2259 if(newver)
2260 free(newver);
2261
2262 mirman_write("mirrors.dat", dbdir, &mdat);
2263 mirman_free(&mdat);
2264 cli_rmdirs(updtmpdir);
2265 return ret;
2266
2267 } else if(ret == 0)
2268 updated = 1;
2269
2270 /* if ipaddr[0] != 0 it will use it to connect to the web host */
2271 if(!optget(opts, "SafeBrowsing")->enabled) {
2272 const char *safedb = NULL;
2273
2274 if(!access("safebrowsing.cvd", R_OK))
2275 safedb = "safebrowsing.cvd";
2276 else if(!access("safebrowsing.cld", R_OK))
2277 safedb = "safebrowsing.cld";
2278
2279 if(safedb) {
2280 if(unlink(safedb))
2281 logg("^SafeBrowsing is disabled but can't remove old %s\n", safedb);
2282 else
2283 logg("*%s removed\n", safedb);
2284 }
2285 } else if((ret = updatedb("safebrowsing", hostname, ipaddr, &signo, opts, dnsreply, localip, outdated, &mdat, logerr, 0)) > 50) {
2286 if(dnsreply)
2287 free(dnsreply);
2288
2289 if(newver)
2290 free(newver);
2291
2292 mirman_write("mirrors.dat", dbdir, &mdat);
2293 mirman_free(&mdat);
2294 cli_rmdirs(updtmpdir);
2295 return ret;
2296 } else if(ret == 0)
2297 updated = 1;
2298
2299 if(!optget(opts, "Bytecode")->enabled) {
2300 const char *dbname = NULL;
2301
2302 if(!access("bytecode.cvd", R_OK))
2303 dbname = "bytecode.cvd";
2304 else if(!access("bytecode.cld", R_OK))
2305 dbname = "bytecode.cld";
2306
2307 if(dbname) {
2308 if(unlink(dbname))
2309 logg("^Bytecode is disabled but can't remove old %s\n", dbname);
2310 else
2311 logg("*%s removed\n", dbname);
2312 }
2313 } else if((ret = updatedb("bytecode", hostname, ipaddr, &signo, opts, dnsreply, localip, outdated, &mdat, logerr, 0)) > 50) {
2314 if(dnsreply)
2315 free(dnsreply);
2316
2317 if(newver)
2318 free(newver);
2319
2320 mirman_write("mirrors.dat", dbdir, &mdat);
2321 mirman_free(&mdat);
2322 cli_rmdirs(updtmpdir);
2323 return ret;
2324 } else if(ret == 0)
2325 updated = 1;
2326 if(dnsreply)
2327 free(dnsreply);
2328
2329 /* handle extra dbs */
2330 if((opt = optget(opts, "ExtraDatabase"))->enabled) {
2331 while(opt) {
2332 if((ret = updatedb(opt->strarg, hostname, ipaddr, &signo, opts, NULL, localip, outdated, &mdat, logerr, 1)) > 50) {
2333 if(newver)
2334 free(newver);
2335 mirman_write("mirrors.dat", dbdir, &mdat);
2336 mirman_free(&mdat);
2337 cli_rmdirs(updtmpdir);
2338 return ret;
2339 } else if(ret == 0)
2340 updated = 1;
2341 opt = opt->nextarg;
2342 }
2343 }
2344
2345 mirman_write("mirrors.dat", dbdir, &mdat);
2346 mirman_free(&mdat);
2347
2348 /* custom dbs */
2349 if((opt = optget(opts, "DatabaseCustomURL"))->enabled) {
2350 while(opt) {
2351 if(updatecustomdb(opt->strarg, &signo, opts, localip, logerr) == 0)
2352 updated = 1;
2353 opt = opt->nextarg;
2354 }
2355 }
2356
2357 cli_rmdirs(updtmpdir);
2358
2359 if(checkdbdir() < 0) {
2360 if(newver)
2361 free(newver);
2362 return 54;
2363 }
2364
2365 if(updated) {
2366 if(optget(opts, "HTTPProxyServer")->enabled || !ipaddr[0]) {
2367 logg("Database updated (%d signatures) from %s\n", signo, hostname);
2368 } else {
2369 logg("Database updated (%d signatures) from %s (IP: %s)\n", signo, hostname, ipaddr);
2370 }
2371
2372#ifdef BUILD_CLAMD
2373 if((opt = optget(opts, "NotifyClamd"))->active)
2374 notify(opt->strarg);
2375#endif
2376
2377 if((opt = optget(opts, "OnUpdateExecute"))->enabled)
2378 execute("OnUpdateExecute", opt->strarg, opts);
2379 }
2380
2381 if(outdated) {
2382 if((opt = optget(opts, "OnOutdatedExecute"))->enabled) {
2383 char *cmd = strdup(opt->strarg);
2384
2385 if((pt = newver)) {
2386 while(*pt) {
2387 if(!strchr("0123456789.", *pt)) {
2388 logg("!downloadmanager: OnOutdatedExecute: Incorrect version number string\n");
2389 free(newver);
2390 newver = NULL;
2391 break;
2392 }
2393 pt++;
2394 }
2395 }
2396
2397 if(newver && (pt = strstr(cmd, "%v"))) {
2398 char *buffer = (char *) malloc(strlen(cmd) + strlen(newver) + 10);
2399
2400 if(!buffer) {
2401 logg("!downloadmanager: Can't allocate memory for buffer\n");
2402 free(cmd);
2403 if(newver)
2404 free(newver);
2405 return 75;
2406 }
2407
2408 *pt = 0; pt += 2;
2409 strcpy(buffer, cmd);
2410 strcat(buffer, newver);
2411 strcat(buffer, pt);
2412 free(cmd);
2413 cmd = strdup(buffer);
2414 free(buffer);
2415 }
2416
2417 if(newver)
2418 execute("OnOutdatedExecute", cmd, opts);
2419
2420 free(cmd);
2421 }
2422 }
2423
2424 if(newver)
2425 free(newver);
2426
2427 return 0;
2428}
Note: See TracBrowser for help on using the repository browser.