PageRenderTime 310ms CodeModel.GetById 137ms app.highlight 147ms RepoModel.GetById 1ms app.codeStats 2ms

/clamav-0.97.5/freshclam/manager.c

#
C | 2446 lines | 1997 code | 323 blank | 126 comment | 631 complexity | 2cfdf99bf35e52c9b6e526790e60947d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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 new %s.cld - please check if there is enough disk space available\n", dbname);
1392	    if(!strcmp(dbname, "main") || !strcmp(dbname, "safebrowsing"))
1393		logg("Updates to main.cvd or safebrowsing.cvd may require 200MB of disk space or more\n");
1394	    err = 1;
1395	}
1396    }
1397
1398    if(!err && !access(info, R_OK)) {
1399	if(tar_addfile(fd, gzs, info) == -1) {
1400	    logg("!buildcld: Can't add %s to new %s.cld - please check if there is enough disk space available\n", info, dbname);
1401	    if(!strcmp(dbname, "main") || !strcmp(dbname, "safebrowsing"))
1402		logg("Updates to main.cvd or safebrowsing.cvd may require 200MB of disk space or more\n");
1403	    err = 1;
1404	}
1405    }
1406
1407    if(!err && !access("daily.cfg", R_OK)) {
1408	if(tar_addfile(fd, gzs, "daily.cfg") == -1) {
1409	    logg("!buildcld: Can't add daily.cfg to new %s.cld - please check if there is enough disk space available\n", dbname);
1410	    err = 1;
1411	}
1412    }
1413
1414    if(err) {
1415	CHDIR_ERR(cwd);
1416	if(gzs)
1417	    gzclose(gzs);
1418	else
1419	    close(fd);
1420	closedir(dir);
1421	unlink(newfile);
1422	return -1;
1423    }
1424
1425    while((dent = readdir(dir))) {
1426	if(dent->d_ino)
1427	{
1428	    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))
1429		continue;
1430
1431	    if(tar_addfile(fd, gzs, dent->d_name) == -1) {
1432		logg("!buildcld: Can't add %s to new %s.cld - please check if there is enough disk space available\n", dent->d_name, dbname);
1433		if(!strcmp(dbname, "main") || !strcmp(dbname, "safebrowsing"))
1434		    logg("Updates to main.cvd or safebrowsing.cvd may require 200MB of disk space or more\n");
1435		CHDIR_ERR(cwd);
1436		if(gzs)
1437		    gzclose(gzs);
1438		else
1439		    close(fd);
1440		closedir(dir);
1441		unlink(newfile);
1442		return -1;
1443	    }
1444	}
1445    }
1446    closedir(dir);
1447
1448    if(gzs) {
1449	if(gzclose(gzs)) {
1450	    logg("!buildcld: gzclose() failed for %s\n", newfile);
1451	    unlink(newfile);
1452	    return -1;
1453	}
1454    } else {
1455	if(close(fd) == -1) {
1456	    logg("!buildcld: close() failed for %s\n", newfile);
1457	    unlink(newfile);
1458	    return -1;
1459	}
1460    }
1461
1462    if(chdir(cwd) == -1) {
1463	logg("!buildcld: Can't return to previous directory %s\n", cwd);
1464	return -1;
1465    }
1466
1467    return 0;
1468}
1469
1470static int test_database(const char *newfile, const char *newdb, int bytecode)
1471{
1472    struct cl_engine *engine;
1473    unsigned newsigs = 0;
1474    int ret;
1475
1476    logg("*Loading signatures from %s\n", newdb);
1477    if(!(engine = cl_engine_new())) {
1478	return 55;
1479    }
1480
1481    if((ret = cl_load(newfile, engine, &newsigs, CL_DB_PHISHING | CL_DB_PHISHING_URLS | CL_DB_BYTECODE | CL_DB_PUA)) != CL_SUCCESS) {
1482	logg("!Failed to load new database: %s\n", cl_strerror(ret));
1483	cl_engine_free(engine);
1484	return 55;
1485    }
1486    if(bytecode && (ret = cli_bytecode_prepare2(engine, &engine->bcs, engine->dconf->bytecode/*FIXME: dconf has no sense here*/))) {
1487	logg("!Failed to compile/load bytecode: %s\n", cl_strerror(ret));
1488	cl_engine_free(engine);
1489	return 55;
1490    }
1491    logg("*Properly loaded %u signatures from new %s\n", newsigs, newdb);
1492    if(engine->domainlist_matcher && engine->domainlist_matcher->sha256_pfx_set.keys)
1493	cli_hashset_destroy(&engine->domainlist_matcher->sha256_pfx_set);
1494    cl_engine_free(engine);
1495    return 0;
1496}
1497
1498#ifndef WIN32
1499static int test_database_wrap(const char *file, const char *newdb, int bytecode)
1500{
1501    char firstline[256];
1502    char lastline[256];
1503    int pipefd[2];
1504    pid_t pid;
1505    int status = 0, ret;
1506    FILE *f;
1507
1508    if (pipe(pipefd) == -1) {
1509	logg("^pipe() failed: %s\n", strerror(errno));
1510	return test_database(file, newdb, bytecode);
1511    }
1512
1513    switch ( pid = fork() ) {
1514	case 0:
1515	    close(pipefd[0]);
1516	    dup2(pipefd[1], 2);
1517	    exit(test_database(file, newdb, bytecode));
1518	case -1:
1519	    close(pipefd[0]);
1520	    close(pipefd[1]);
1521	    logg("^fork() failed: %s\n", strerror(errno));
1522	    return test_database(file, newdb, bytecode);
1523	default:
1524	    /* read first / last line printed by child*/
1525	    close(pipefd[1]);
1526	    f = fdopen(pipefd[0], "r");
1527	    firstline[0] = 0;
1528	    lastline[0] = 0;
1529	    do {
1530		if (!fgets(firstline, sizeof(firstline), f))
1531		    break;
1532		/* ignore warning messages, otherwise the outdated warning will
1533		 * make us miss the important part of the error message */
1534	    } while (!strncmp(firstline, "LibClamAV Warning:", 18));
1535	    /* must read entire output, child doesn't like EPIPE */
1536	    while (fgets(lastline, sizeof(firstline), f)) {
1537		/* print the full output only when LogVerbose or -v is given */
1538		logg("*%s", lastline);
1539	    }
1540	    fclose(f);
1541
1542	    while ((ret = waitpid(pid, &status, 0)) == -1 && errno == EINTR);
1543	    if (ret == -1 && errno != ECHILD)
1544		logg("^waitpid() failed: %s\n", strerror(errno));
1545	    cli_chomp(firstline);
1546	    cli_chomp(lastline);
1547	    if (firstline[0]) {
1548		logg("!During database load : %s%s%s\n",
1549		     firstline, lastline[0] ? " [...] " : "",
1550		     lastline);
1551	    }
1552	    if (WIFEXITED(status)) {
1553		ret = WEXITSTATUS(status);
1554		if (ret) {
1555		    logg("^Database load exited with status %d\n", ret);
1556		    return ret;
1557		}
1558		if (firstline[0])
1559		    logg("^Database successfully loaded, but there is stderr output\n");
1560		return 0;
1561	    }
1562	    if (WIFSIGNALED(status)) {
1563		logg("!Database load killed by signal %d\n", WTERMSIG(status));
1564		return 55;
1565	    }
1566	    logg("^Unknown status from wait: %d\n", status);
1567	    return 55;
1568    }
1569}
1570#else
1571static int test_database_wrap(const char *file, const char *newdb, int bytecode)
1572{
1573    int ret = 55;
1574    __try
1575    {
1576	ret = test_database(file, newdb, bytecode);
1577    }
1578    __except (logg("!Exception during database testing, code %08x\n",
1579		 GetExceptionCode()),
1580	      EXCEPTION_CONTINUE_SEARCH)
1581    { }
1582    return ret;
1583}
1584#endif
1585
1586static int checkdbdir(void)
1587{
1588	DIR *dir;
1589	struct dirent *dent;
1590	char fname[512], broken[513];
1591	int ret, fret = 0;
1592
1593    if(!(dir = opendir(dbdir))) {
1594	logg("!checkdbdir: Can't open directory %s\n", dbdir);
1595	return -1;
1596    }
1597
1598    while((dent = readdir(dir))) {
1599	if(dent->d_ino) {
1600	    if(cli_strbcasestr(dent->d_name, ".cld") || cli_strbcasestr(dent->d_name, ".cvd")) {
1601		snprintf(fname, sizeof(fname), "%s"PATHSEP"%s", dbdir, dent->d_name);
1602		if((ret = cl_cvdverify(fname))) {
1603		    fret = -1;
1604		    mprintf("!Corrupted database file %s: %s\n", fname, cl_strerror(ret));
1605		    snprintf(broken, sizeof(broken), "%s.broken", fname);
1606		    if(!access(broken, R_OK))
1607			unlink(broken);
1608		    if(rename(fname, broken)) {
1609			if(unlink(fname))
1610			    mprintf("!Can't remove broken database file %s, please delete it manually and restart freshclam\n", fname);
1611		    } else {
1612			mprintf("Corrupted database file renamed to %s\n", broken);
1613		    }
1614		}
1615	    }
1616	}
1617    }
1618
1619    closedir(dir);
1620    return fret;
1621}
1622
1623extern int sigchld_wait;
1624
1625static 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)
1626{
1627	struct cl_cvd *current, *remote;
1628	const struct optstruct *opt;
1629	unsigned int nodb = 0, currver = 0, newver = 0, port = 0, i, j;
1630	int ret, ims = -1;
1631	char *pt, cvdfile[32], localname[32], *tmpdir = NULL, *newfile, *newfile2, newdb[32];
1632	char extradbinfo[64], *extradnsreply = NULL, squery[64];
1633	const char *proxy = NULL, *user = NULL, *pass = NULL, *uas = NULL;
1634	unsigned int flevel = cl_retflevel(), remote_flevel = 0, maxattempts;
1635	unsigned int can_whitelist = 0, mirror_stats = 0;
1636#ifdef _WIN32
1637	unsigned int w32 = 1;
1638#else
1639	unsigned int w32 = 0;
1640#endif
1641	int ctimeout, rtimeout;
1642
1643
1644    if(cli_strbcasestr(hostname, ".clamav.net"))
1645	mirror_stats = 1;
1646
1647    snprintf(cvdfile, sizeof(cvdfile), "%s.cvd", dbname);
1648
1649    if(!(current = currentdb(dbname, localname))) {
1650	nodb = 1;
1651    } else {
1652	mdat->dbflevel = current->fl;
1653    }
1654
1655    if(!nodb && !extra && dnsreply) {
1656	    int field = 0;
1657
1658	if(!strcmp(dbname, "main")) {
1659	    field = 1;
1660	} else if(!strcmp(dbname, "daily")) {
1661	    field = 2;
1662	} else if(!strcmp(dbname, "safebrowsing")) {
1663	    field = 6;
1664	} else if(!strcmp(dbname, "bytecode")) {
1665	    field = 7;
1666	} else {
1667	    logg("!updatedb: Unknown database name (%s) passed.\n", dbname);
1668	    cl_cvdfree(current);
1669	    return 70;
1670	}
1671
1672	if(field && (pt = cli_strtok(dnsreply, field, ":"))) {
1673	    if(!cli_isnumber(pt)) {
1674		logg("^Broken database version in TXT record.\n");
1675	    } else {
1676		newver = atoi(pt);
1677		logg("*%s version from DNS: %d\n", cvdfile, newver);
1678	    }
1679	    free(pt);
1680	} else {
1681	    logg("^Invalid DNS reply. Falling back to HTTP mode.\n");
1682	}
1683    }
1684#ifdef HAVE_RESOLV_H
1685    else if(!nodb && extra && !optget(opts, "no-dns")->enabled) {
1686	snprintf(extradbinfo, sizeof(extradbinfo), "%s.cvd.clamav.net", dbname);
1687	if((extradnsreply = dnsquery(extradbinfo, T_TXT, NULL))) {
1688	    if((pt = cli_strtok(extradnsreply, 1, ":"))) {
1689		    int rt;
1690		    time_t ct;
1691
1692		rt = atoi(pt);
1693		free(pt);
1694		time(&ct);
1695		if((int) ct - rt > 10800) {
1696		    logg("^DNS record is older than 3 hours.\n");
1697		    free(extradnsreply);
1698		    extradnsreply = NULL;
1699		}
1700	    } else {
1701		logg("^No timestamp in TXT record for %s\n", cvdfile);
1702		free(extradnsreply);
1703		extradnsreply = NULL;
1704	    }
1705	    if((pt = cli_strtok(extradnsreply, 0, ":"))) {
1706		if(!cli_isnumber(pt)) {
1707		    logg("^Broken database version in TXT record for %s\n", cvdfile);
1708		} else {
1709		    newver = atoi(pt);
1710		    logg("*%s version from DNS: %d\n", cvdfile, newver);
1711		}
1712		free(pt);
1713	    } else {
1714		logg("^Invalid DNS reply. Falling back to HTTP mode.\n");
1715	    }
1716	}
1717    }
1718#endif
1719
1720    if(dnsreply && !extra) {
1721	if((pt = cli_strtok(dnsreply, 5, ":"))) {
1722	    remote_flevel = atoi(pt);
1723	    free(pt);
1724	    if(remote_flevel && (remote_flevel - flevel < 4))
1725		can_whitelist = 1;
1726	}
1727    }
1728
1729    /* Initialize proxy settings */
1730    if((opt = optget(opts, "HTTPProxyServer"))->enabled) {
1731	proxy = opt->strarg;
1732	if(strncasecmp(proxy, "http://", 7) == 0)
1733	    proxy += 7;
1734
1735	if((opt = optget(opts, "HTTPProxyUsername"))->enabled) {
1736	    user = opt->strarg;
1737	    if((opt = optget(opts, "HTTPProxyPassword"))->enabled) {
1738		pass = opt->strarg;
1739	    } else {
1740		logg("HTTPProxyUsername requires HTTPProxyPassword\n");
1741		if(current)
1742		    cl_cvdfree(current);
1743		return 56;
1744	    }
1745	}
1746
1747	if((opt = optget(opts, "HTTPProxyPort"))->enabled)
1748	    port = opt->numarg;
1749
1750	logg("Connecting via %s\n", proxy);
1751    }
1752
1753    if((opt = optget(opts, "HTTPUserAgent"))->enabled)
1754	uas = opt->strarg;
1755
1756    ctimeout = optget(opts, "ConnectTimeout")->numarg;
1757    rtimeout = optget(opts, "ReceiveTimeout")->numarg;
1758
1759    if(!nodb && !newver) {
1760
1761	remote = remote_cvdhead(cvdfile, localname, hostname, ip, localip, proxy, port, user, pass, uas, &ims, ctimeout, rtimeout, mdat, logerr, can_whitelist);
1762
1763	if(!nodb && !ims) {
1764	    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);
1765	    *signo += current->sigs;
1766#ifdef HAVE_RESOLV_H
1767	    if(mirror_stats && strlen(ip)) {
1768		snprintf(squery, sizeof(squery), "%s.%u.%u.%u.%u.%s.ping.clamav.net", dbname, current->version, flevel, 1, w32, ip);
1769		dnsquery(squery, T_A, NULL);
1770	    }
1771#endif
1772	    cl_cvdfree(current);
1773	    return 1;
1774	}
1775
1776	if(!remote) {
1777	    logg("^Can't read %s header from %s (IP: %s)\n", cvdfile, hostname, ip);
1778#ifdef HAVE_RESOLV_H
1779	    if(mirror_stats && strlen(ip)) {
1780		snprintf(squery, sizeof(squery), "%s.%u.%u.%u.%u.%s.ping.clamav.net", dbname, current->version + 1, flevel, 0, w32, ip);
1781		dnsquery(squery, T_A, NULL);
1782	    }
1783#endif
1784	    cl_cvdfree(current);
1785	    return 58;
1786	}
1787
1788	newver = remote->version;
1789	cl_cvdfree(remote);
1790    }
1791
1792    if(!nodb && (current->version >= newver)) {
1793	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);
1794
1795	if(!outdated && flevel < current->fl) {
1796	    /* display warning even for already installed database */
1797	    logg("^Current functionality level = %d, recommended = %d\n", flevel, current->fl);
1798	    logg("Please check if ClamAV tools are linked against the proper version of libclamav\n");
1799	    logg("DON'T PANIC! Read http://www.clamav.net/support/faq\n");
1800	}
1801
1802	*signo += current->sigs;
1803	cl_cvdfree(current);
1804	return 1;
1805    }
1806
1807    if(current) {
1808	currver = current->version;
1809	cl_cvdfree(current);
1810    }
1811
1812    /*
1813    if(ipaddr[0]) {
1814	hostfd = wwwconnect(ipaddr, proxy, port, NULL, localip);
1815    } else {
1816	hostfd = wwwconnect(hostname, proxy, port, ipaddr, localip);
1817	if(!ip[0])
1818	    strcpy(ip, ipaddr);
1819    }
1820
1821    if(hostfd < 0) {
1822	if(ipaddr[0])
1823	    logg("Connection with %s (IP: %s) failed.\n", hostname, ipaddr);
1824	else
1825	    logg("Connection with %s failed.\n", hostname);
1826	return 52;
1827    };
1828    */
1829
1830    if(!optget(opts, "ScriptedUpdates")->enabled)
1831	nodb = 1;
1832
1833    newfile = cli_gentemp(updtmpdir);
1834    if(nodb) {
1835	ret = 

Large files files are truncated, but you can click here to view the full file