libclamav/readdb.c
e3aaff8e
 /*
2023340a
  *  Copyright (C) 2007-2008 Sourcefire, Inc.
4addba22
  *
2023340a
  *  Authors: Tomasz Kojm
e3aaff8e
  *
  *  This program is free software; you can redistribute it and/or modify
bb34cb31
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
e3aaff8e
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
e3aaff8e
  */
 
6d6e8271
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
b58fdfc2
 #ifdef	HAVE_UNISTD_H
e3aaff8e
 #include <unistd.h>
b58fdfc2
 #endif
d3a6556d
 #ifndef C_WINDOWS
e3aaff8e
 #include <dirent.h>
b58fdfc2
 #endif
e3aaff8e
 #include <sys/types.h>
 #include <sys/stat.h>
b58fdfc2
 #ifdef	HAVE_SYS_PARAM_H
15edd45f
 #include <sys/param.h>
b58fdfc2
 #endif
e3aaff8e
 #include <fcntl.h>
056d95dc
 #include <zlib.h>
e3aaff8e
 
 #include "clamav.h"
e4ae7726
 #include "cvd.h"
b58fdfc2
 #ifdef	HAVE_STRINGS_H
 #include <strings.h>
 #endif
8000d078
 #include "matcher-ac.h"
 #include "matcher-bm.h"
bedc58de
 #include "matcher.h"
e3aaff8e
 #include "others.h"
 #include "str.h"
bc93eda0
 #include "dconf.h"
7021b545
 #include "filetypes.h"
 #include "filetypes_int.h"
fc83da82
 #include "readdb.h"
677fc4ba
 #include "cltypes.h"
4367454d
 #include "default.h"
e3aaff8e
 
fc83da82
 #include "phishcheck.h"
7dec7955
 #include "phish_whitelist.h"
 #include "phish_domaincheck_db.h"
3da4dd4c
 #include "regex_list.h"
c3671221
 #include "hashtab.h"
7dec7955
 
2bb229f6
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 #include <limits.h>
88794204
 #include <stddef.h>
2bb229f6
 #endif
 
b94e66c4
 #include "mpool.h"
 
f51e962f
 #ifdef CL_THREAD_SAFE
 #  include <pthread.h>
 static pthread_mutex_t cli_ref_mutex = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
ed9753e9
 struct cli_ignsig {
     char *dbname, *signame;
     unsigned int line;
     struct cli_ignsig *next;
 };
 
 struct cli_ignored {
     struct hashset hs;
     struct cli_ignsig *list;
 };
 
d6e1ef16
 char *cli_virname(char *virname, unsigned int official)
b5513f8d
 {
 	char *newname, *pt;
 
 
     if(!virname)
 	return NULL;
 
     if((pt = strstr(virname, " (Clam)")))
d6e1ef16
 	*pt='\0';
b5513f8d
 
d6e1ef16
     if(!virname[0]) {
b5513f8d
 	cli_errmsg("cli_virname: Empty virus name\n");
 	return NULL;
     }
 
d6e1ef16
     if(official)
         return cli_strdup(virname);
b5513f8d
 
d6e1ef16
     newname = (char *) cli_malloc(strlen(virname) + 11 + 1);
     if(!newname) {
       cli_errmsg("cli_virname: Can't allocate memory for newname\n");
       return NULL;
b5513f8d
     }
d6e1ef16
     sprintf(newname, "%s.UNOFFICIAL", virname);
     return newname;
b5513f8d
 }
 
ec285505
 int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hexsig, uint16_t rtype, uint16_t type, const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options)
888f5794
 {
8000d078
 	struct cli_bm_patt *bm_new;
084ee140
 	char *pt, *hexcpy, *start, *n;
b5513f8d
 	int ret, asterisk = 0;
33f89aa5
 	unsigned int i, j, len, parts = 0;
084ee140
 	int mindist = 0, maxdist = 0, error = 0;
 
888f5794
 
084ee140
     if(strchr(hexsig, '{')) {
888f5794
 
8000d078
 	root->ac_partsigs++;
084ee140
 
6f38c939
 	if(!(hexcpy = cli_strdup(hexsig)))
084ee140
 	    return CL_EMEM;
 
 	len = strlen(hexsig);
 	for(i = 0; i < len; i++)
a8f79297
 	    if(hexsig[i] == '{' || hexsig[i] == '*')
084ee140
 		parts++;
 
 	if(parts)
 	    parts++;
 
 	start = pt = hexcpy;
 	for(i = 1; i <= parts; i++) {
 
 	    if(i != parts) {
a8f79297
 		for(j = 0; j < strlen(start); j++) {
 		    if(start[j] == '{') {
 			asterisk = 0;
 			pt = start + j;
 			break;
 		    }
 		    if(start[j] == '*') {
 			asterisk = 1;
 			pt = start + j;
 			break;
 		    }
 		}
084ee140
 		*pt++ = 0;
 	    }
 
341faf60
 	    if((ret = cli_ac_addsig(root, virname, start, root->ac_partsigs, parts, i, rtype, type, mindist, maxdist, offset, lsigid, options))) {
a8f79297
 		cli_errmsg("cli_parse_add(): Problem adding signature (1).\n");
084ee140
 		error = 1;
 		break;
 	    }
 
 	    if(i == parts)
 		break;
 
a8f79297
 	    mindist = maxdist = 0;
 
 	    if(asterisk) {
 		start = pt;
 		continue;
 	    }
 
084ee140
 	    if(!(start = strchr(pt, '}'))) {
 		error = 1;
 		break;
 	    }
 	    *start++ = 0;
 
 	    if(!pt) {
 		error = 1;
 		break;
 	    }
 
 	    if(!strchr(pt, '-')) {
a3fe2c5b
 		if(!cli_isnumber(pt) || (mindist = maxdist = atoi(pt)) < 0) {
084ee140
 		    error = 1;
 		    break;
 		}
 	    } else {
b68d11d2
 		if((n = cli_strtok(pt, 0, "-"))) {
a3fe2c5b
 		    if(!cli_isnumber(n) || (mindist = atoi(n)) < 0) {
084ee140
 			error = 1;
 			free(n);
 			break;
 		    }
 		    free(n);
 		}
 
b68d11d2
 		if((n = cli_strtok(pt, 1, "-"))) {
a3fe2c5b
 		    if(!cli_isnumber(n) || (maxdist = atoi(n)) < 0) {
084ee140
 			error = 1;
 			free(n);
 			break;
 		    }
 		    free(n);
 		}
a3fe2c5b
 
 		if((n = cli_strtok(pt, 2, "-"))) { /* strict check */
 		    error = 1;
 		    free(n);
9b0004bf
 		    break;
a3fe2c5b
 		}
084ee140
 	    }
 	}
 
 	free(hexcpy);
 	if(error)
 	    return CL_EMALFDB;
 
     } else if(strchr(hexsig, '*')) {
8000d078
 	root->ac_partsigs++;
888f5794
 
 	len = strlen(hexsig);
 	for(i = 0; i < len; i++)
 	    if(hexsig[i] == '*')
 		parts++;
 
8000d078
 	if(parts)
888f5794
 	    parts++;
 
 	for(i = 1; i <= parts; i++) {
 	    if((pt = cli_strtok(hexsig, i - 1, "*")) == NULL) {
3ed92fe1
 		cli_errmsg("Can't extract part %d of partial signature.\n", i);
888f5794
 		return CL_EMALFDB;
 	    }
 
341faf60
 	    if((ret = cli_ac_addsig(root, virname, pt, root->ac_partsigs, parts, i, rtype, type, 0, 0, offset, lsigid, options))) {
a8f79297
 		cli_errmsg("cli_parse_add(): Problem adding signature (2).\n");
888f5794
 		free(pt);
 		return ret;
 	    }
 
 	    free(pt);
 	}
 
677fc4ba
     } else if(root->ac_only || strpbrk(hexsig, "?(") || type || lsigid) {
341faf60
 	if((ret = cli_ac_addsig(root, virname, hexsig, 0, 0, 0, rtype, type, 0, 0, offset, lsigid, options))) {
a8f79297
 	    cli_errmsg("cli_parse_add(): Problem adding signature (3).\n");
8000d078
 	    return ret;
 	}
 
     } else {
47d40feb
 	bm_new = (struct cli_bm_patt *) mpool_calloc(root->mempool, 1, sizeof(struct cli_bm_patt));
8000d078
 	if(!bm_new)
 	    return CL_EMEM;
47d40feb
         bm_new->pattern = (unsigned char *) cli_mpool_hex2str(root->mempool, hexsig);
b583d6ed
 	if(!bm_new->pattern) {
47d40feb
 	    mpool_free(root->mempool, bm_new);
8000d078
 	    return CL_EMALFDB;
091df0e3
 	}
8000d078
 	bm_new->length = strlen(hexsig) / 2;
 
47d40feb
 	bm_new->virname = cli_mpool_virname(root->mempool, (char *) virname, options & CL_DB_OFFICIAL);
b5513f8d
 	if(!bm_new->virname) {
47d40feb
 	    mpool_free(root->mempool, bm_new->pattern);
 	    mpool_free(root->mempool, bm_new);
8000d078
 	    return CL_EMEM;
 	}
 
6f38c939
 	if(offset) {
47d40feb
 	    bm_new->offset = cli_mpool_strdup(root->mempool, offset);
6f38c939
 	    if(!bm_new->offset) {
47d40feb
 	        mpool_free(root->mempool, bm_new->pattern);
 		mpool_free(root->mempool, bm_new->virname);
 		mpool_free(root->mempool, bm_new);
6f38c939
 		return CL_EMEM;
 	    }
c889f9cf
 	}
 
b68d11d2
 	bm_new->target = target;
 
8000d078
 	if(bm_new->length > root->maxpatlen)
 	    root->maxpatlen = bm_new->length;
 
 	if((ret = cli_bm_addpatt(root, bm_new))) {
a8f79297
 	    cli_errmsg("cli_parse_add(): Problem adding signature (4).\n");
47d40feb
 	    mpool_free(root->mempool, bm_new->pattern);
 	    mpool_free(root->mempool, bm_new->virname);
 	    mpool_free(root->mempool, bm_new);
888f5794
 	    return ret;
 	}
     }
 
8d3aca30
     return CL_SUCCESS;
888f5794
 }
e3aaff8e
 
83fa5305
 static int cli_initroots(struct cl_engine *engine, unsigned int options)
5612732c
 {
 	int i, ret;
 	struct cli_matcher *root;
 
 
4addba22
     for(i = 0; i < CLI_MTARGETS; i++) {
5612732c
 	if(!engine->root[i]) {
 	    cli_dbgmsg("Initializing engine->root[%d]\n", i);
47d40feb
 	    root = engine->root[i] = (struct cli_matcher *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_matcher));
b94e66c4
 #ifdef USE_MPOOL
 	    root->mempool = engine->mempool;
 #endif
5612732c
 	    if(!root) {
0a3d4094
 		cli_errmsg("cli_initroots: Can't allocate memory for cli_matcher\n");
5612732c
 		return CL_EMEM;
 	    }
 
ab0d2f05
 	    if(cli_mtargets[i].ac_only || engine->ac_only)
83fa5305
 		root->ac_only = 1;
 
5612732c
 	    cli_dbgmsg("Initialising AC pattern matcher of root[%d]\n", i);
ab0d2f05
 	    if((ret = cli_ac_init(root, engine->ac_mindepth, engine->ac_maxdepth))) {
5612732c
 		/* no need to free previously allocated memory here */
0a3d4094
 		cli_errmsg("cli_initroots: Can't initialise AC pattern matcher\n");
fbcef1b0
 		return ret;
5612732c
 	    }
 
83fa5305
 	    if(!root->ac_only) {
0a3d4094
 		cli_dbgmsg("cli_initroots: Initializing BM tables of root[%d]\n", i);
83fa5305
 		if((ret = cli_bm_init(root))) {
0a3d4094
 		    cli_errmsg("cli_initroots: Can't initialise BM pattern matcher\n");
83fa5305
 		    return ret;
 		}
5612732c
 	    }
8000d078
 	}
     }
 
8d3aca30
     return CL_SUCCESS;
5612732c
 }
 
e8ae4fae
 char *cli_dbgets(char *buff, unsigned int size, FILE *fs, struct cli_dbio *dbio)
056d95dc
 {
     if(fs) {
 	return fgets(buff, size, fs);
 
     } else {
 	    char *pt;
 	    unsigned int bs;
 
e8ae4fae
 	if(!dbio->size)
056d95dc
 	    return NULL;
 
e8ae4fae
 	bs = dbio->size < size ? dbio->size + 1 : size;
 	if(dbio->gzs)
 	    pt = gzgets(dbio->gzs, buff, bs);
 	else
 	    pt = fgets(buff, bs, dbio->fs);
 
 	dbio->size -= strlen(buff);
056d95dc
 	if(!pt)
 	    cli_errmsg("cli_dbgets: Preliminary end of data\n");
 	return pt;
     }
 }
 
ed9753e9
 static int cli_chkign(const struct cli_ignored *ignored, const char *dbname, unsigned int line, const char *signame)
 {
 	struct cli_ignsig *pt;
 
     if(!ignored || !dbname || !signame)
 	return 0;
 
     if(hashset_contains(&ignored->hs, line)) {
 	pt = ignored->list;
 	while(pt) {
 	    if(pt->line == line && !strcmp(pt->dbname, dbname) && !strcmp(pt->signame, signame)) {
 		cli_dbgmsg("Skipping signature %s @ %s:%u\n", signame, dbname, line);
 		return 1;
 	    }
 	    pt = pt->next;
 	}
     }
 
     return 0;
 }
 
b023c36d
 static int cli_chkpua(const char *signame, const char *pua_cats, unsigned int options)
 {
 	char cat[32], *pt;
 	const char *sig;
 	int ret;
 
     if(strncmp(signame, "PUA.", 4)) {
 	cli_dbgmsg("Skipping signature %s - no PUA prefix\n", signame);
 	return 1;
     }
     sig = signame + 3;
     if(!(pt = strchr(sig + 1, '.'))) {
 	cli_dbgmsg("Skipping signature %s - bad syntax\n", signame);
 	return 1;
     }
 
a45c7039
     if((unsigned int) (pt - sig + 2) > sizeof(cat)) {
b023c36d
 	cli_dbgmsg("Skipping signature %s - too long category name\n", signame);
 	return 1;
     }
 
     strncpy(cat, sig, pt - signame + 1);
     cat[pt - sig + 1] = 0;
     pt = strstr(pua_cats, cat);
 
     if(options & CL_DB_PUA_INCLUDE)
 	ret = pt ? 0 : 1;
     else
 	ret = pt ? 1 : 0;
 
     if(ret)
 	cli_dbgmsg("Skipping PUA signature %s - excluded category\n", signame);
 
     return ret;
 }
 
15850fc6
 static int cli_loaddb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
5612732c
 {
 	char buffer[FILEBUFF], *pt, *start;
ed9753e9
 	unsigned int line = 0, sigs = 0;
 	int ret = 0;
5612732c
 	struct cli_matcher *root;
 
 
15850fc6
     if((ret = cli_initroots(engine, options)))
5612732c
 	return ret;
 
15850fc6
     root = engine->root[0];
5612732c
 
e8ae4fae
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
4048c4f6
 	line++;
 	cli_chomp(buffer);
 
 	pt = strchr(buffer, '=');
 	if(!pt) {
 	    cli_errmsg("Malformed pattern line %d\n", line);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	start = buffer;
 	*pt++ = 0;
ee039e40
 
15850fc6
 	if(engine->ignored && cli_chkign(engine->ignored, dbname, line, start))
ed9753e9
 	    continue;
 
4048c4f6
 	if(*pt == '=') continue;
ee039e40
 
677fc4ba
 	if((ret = cli_parse_add(root, start, pt, 0, 0, NULL, 0, NULL, options))) {
b68d11d2
 	    ret = CL_EMALFDB;
 	    break;
 	}
ed9753e9
 	sigs++;
b68d11d2
     }
 
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %d\n", line);
 	return ret;
     }
 
     if(signo)
ed9753e9
 	*signo += sigs;
b68d11d2
 
8d3aca30
     return CL_SUCCESS;
b68d11d2
 }
 
15850fc6
 static int cli_loadwdb(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio)
3da4dd4c
 {
 	int ret = 0;
 
ec481027
 
15850fc6
     if(!(engine->dconf->phishing & PHISHING_CONF_ENGINE))
692bda68
 	return CL_SUCCESS;
 
15850fc6
     if(!engine->whitelist_matcher) {
 	if((ret = init_whitelist(engine))) {
ec481027
 	    return ret;
3da4dd4c
 	}
ec481027
     }
3da4dd4c
 
03527bee
     if((ret = load_regex_matcher(engine->whitelist_matcher, fs, NULL, options, 1, dbio))) {
ec481027
 	return ret;
     }
3da4dd4c
 
ec481027
     return CL_SUCCESS;
3da4dd4c
 }
 
03527bee
 static int cli_loadpdb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
3da4dd4c
 {
 	int ret = 0;
 
ec481027
 
15850fc6
     if(!(engine->dconf->phishing & PHISHING_CONF_ENGINE))
692bda68
 	return CL_SUCCESS;
 
15850fc6
     if(!engine->domainlist_matcher) {
 	if((ret = init_domainlist(engine))) {
ec481027
 	    return ret;
3da4dd4c
 	}
ec481027
     }
3da4dd4c
 
03527bee
     if((ret = load_regex_matcher(engine->domainlist_matcher, fs, signo, options, 0, dbio))) {
ec481027
 	return ret;
     }
 
     return CL_SUCCESS;
3da4dd4c
 }
ec481027
 
fea1b9bb
 static int cli_checkoffset(const char *offset, unsigned int type)
 {
 	unsigned int foo;
 	const char *pt = offset;
 
     if(isdigit(*offset)) {
 	while(*pt++)
 	    if(!strchr("0123456789,", *pt))
 		return 1;
 	return 0;
     }
 
     if(!strncmp(offset, "EOF-", 4))
 	return 0;
 
     if((type == 1 || type == 6) && (!strncmp(offset, "EP+", 3) || !strncmp(offset, "EP-", 3) || (sscanf(offset, "SL+%u", &foo) == 1) || (sscanf(offset, "S%u+%u", &foo, &foo) == 2)))
 	return 0;
 
     return 1;
 }
 
e4e8366f
 #define NDB_TOKENS 6
15850fc6
 static int cli_loadndb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned short sdb, unsigned int options, struct cli_dbio *dbio, const char *dbname)
b68d11d2
 {
72fb25ea
 	const char *tokens[NDB_TOKENS + 1];
e4e8366f
 	char buffer[FILEBUFF];
 	const char *sig, *virname, *offset, *pt;
5612732c
 	struct cli_matcher *root;
72fb25ea
 	int line = 0, sigs = 0, ret = 0, tokens_count;
b68d11d2
 	unsigned short target;
9f8098c0
 	unsigned int phish = options & CL_DB_PHISHING;
b68d11d2
 
 
15850fc6
     if((ret = cli_initroots(engine, options)))
5612732c
 	return ret;
b68d11d2
 
e8ae4fae
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
b68d11d2
 	line++;
7afdc309
 
 	if(!strncmp(buffer, "Exploit.JPEG.Comment", 20)) /* temporary */
 	    continue;
 
9f8098c0
 	if(!phish)
d6449522
 	    if(!strncmp(buffer, "HTML.Phishing", 13) || !strncmp(buffer, "Email.Phishing", 14))
 		continue;
 
b68d11d2
 	cli_chomp(buffer);
 
72fb25ea
 	tokens_count = cli_strtokenize(buffer, ':', NDB_TOKENS + 1, tokens);
 	/* FIXME: re-enable after fixing invalid sig @ main.ndb:53467 */
 	if(tokens_count < 4 /*|| tokens_count > 6*/) {
b68d11d2
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
72fb25ea
 	virname = tokens[0];
 
15850fc6
 	if(engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)))
 	    if(cli_chkpua(virname, engine->pua_cats, options))
b023c36d
 		continue;
 
15850fc6
 	if(engine->ignored && cli_chkign(engine->ignored, dbname, line, virname))
ed9753e9
 	    continue;
 
72fb25ea
 	if(tokens_count > 4) { /* min version */
 	    pt = tokens[4];
95083a4f
 	    if(!isdigit(*pt)) {
 		ret = CL_EMALFDB;
 		break;
 	    }
 
53721687
 	    if((unsigned int) atoi(pt) > cl_retflevel()) {
38d1d109
 		cli_dbgmsg("Signature for %s not loaded (required f-level: %d)\n", virname, atoi(pt));
95083a4f
 		continue;
 	    }
 
72fb25ea
 	    if(tokens_count == 6) { /* max version */
 		pt = tokens[5];
555c5390
 		if(!isdigit(*pt)) {
 		    ret = CL_EMALFDB;
 		    break;
 		}
 
53721687
 		if((unsigned int) atoi(pt) < cl_retflevel()) {
555c5390
 		    continue;
 		}
 	    }
95083a4f
 	}
 
e4e8366f
 	if(!(pt = tokens[1]) || !isdigit(*pt)) {
b68d11d2
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	target = (unsigned short) atoi(pt);
 
4addba22
 	if(target >= CLI_MTARGETS) {
5612732c
 	    cli_dbgmsg("Not supported target type in signature for %s\n", virname);
 	    continue;
 	}
 
15850fc6
 	root = engine->root[target];
5612732c
 
72fb25ea
 	offset = tokens[2];
 	if(!strcmp(offset, "*"))
7ec67e94
 	    offset = NULL;
b68d11d2
 
fea1b9bb
 	if(offset && cli_checkoffset(offset, target)) {
 	    cli_errmsg("Incorrect offset '%s' for signature type-%u\n", offset, target);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
72fb25ea
 	sig = tokens[3];
b68d11d2
 
677fc4ba
 	if((ret = cli_parse_add(root, virname, sig, 0, 0, offset, target, NULL, options))) {
4048c4f6
 	    ret = CL_EMALFDB;
 	    break;
 	}
ed9753e9
 	sigs++;
ee039e40
     }
 
4048c4f6
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	return CL_EMALFDB;
     }
8139fd99
 
4048c4f6
     if(ret) {
b68d11d2
 	cli_errmsg("Problem parsing database at line %d\n", line);
8139fd99
 	return ret;
     }
 
3805ebcb
     if(signo)
95083a4f
 	*signo += sigs;
4048c4f6
 
15850fc6
     if(sdb && sigs && !engine->sdb) {
 	engine->sdb = 1;
555c5390
 	cli_dbgmsg("*** Self protection mechanism activated.\n");
     }
 
8d3aca30
     return CL_SUCCESS;
4048c4f6
 }
 
677fc4ba
 struct lsig_attrib {
     const char *name;
     unsigned int type;
     void **pt;
 };
 
 /* TODO: rework this */
 static int lsigattribs(char *attribs, struct cli_lsig_tdb *tdb)
 {
 	struct lsig_attrib attrtab[] = {
 #define ATTRIB_TOKENS	2
 	    { "Target",	    CLI_TDB_UINT,	(void **) &tdb->target	    },
 	    { "Engine",	    CLI_TDB_RANGE,	(void **) &tdb->engine	    },
 /*
 	    { "NoS",	    CLI_TDB_RANGE,	(void **) &tdb->nos	    },
 	    { "EP",	    CLI_TDB_RANGE,	(void **) &tdb->ep	    },
 	    { "SectOff",    CLI_TDB_RANGE2,	(void **) &tdb->sectoff	    },
 	    { "SectRVA",    CLI_TDB_RANGE2,	(void **) &tdb->sectrva	    },
 	    { "SectVSZ",    CLI_TDB_RANGE2,	(void **) &tdb->sectvsz	    },
 	    { "SectRAW",    CLI_TDB_RANGE2,	(void **) &tdb->sectraw	    },
 	    { "SectRSZ",    CLI_TDB_RANGE2,	(void **) &tdb->sectrsz	    },
 	    { "SectURVA",   CLI_TDB_RANGE2,	(void **) &tdb->secturva    },
 	    { "SectUVSZ",   CLI_TDB_RANGE2,	(void **) &tdb->sectuvsz    },
 	    { "SectURAW",   CLI_TDB_RANGE2,	(void **) &tdb->secturaw    },
 	    { "SectURSZ",   CLI_TDB_RANGE2,	(void **) &tdb->sectursz    },
 */
 	    { NULL,	    0,			NULL,			    }
 	};
 	struct lsig_attrib *apt;
 	char *tokens[ATTRIB_TOKENS], *pt, *pt2;
72fb25ea
 	unsigned int v1, v2, v3, i, j, tokens_count;
677fc4ba
 	uint32_t cnt, off[ATTRIB_TOKENS];
 
 
72fb25ea
     tokens_count = cli_strtokenize(attribs, ',', ATTRIB_TOKENS, (const char **) tokens);
677fc4ba
 
72fb25ea
     for(i = 0; i < tokens_count; i++) {
677fc4ba
 	if(!(pt = strchr(tokens[i], ':'))) {
 	    cli_errmsg("lsigattribs: Incorrect format of attribute '%s'\n", tokens[i]);
 	    return -1;
 	}
 	*pt++ = 0;
 
 	apt = NULL;
 	for(j = 0; attrtab[j].name; j++) {
 	    if(!strcmp(attrtab[j].name, tokens[i])) {
 		apt = &attrtab[j];
 		break;
 	    }
 	}
 
 	if(!apt) {
 	    cli_dbgmsg("lsigattribs: Unknown attribute name '%s'\n", tokens[i]);
 	    continue;
 	}
 
 	switch(apt->type) {
 	    case CLI_TDB_UINT:
 		off[i] = cnt = tdb->cnt[CLI_TDB_UINT]++;
47d40feb
 		tdb->val = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->val, tdb->cnt[CLI_TDB_UINT] * sizeof(uint32_t));
677fc4ba
 		if(!tdb->val) {
 		    tdb->cnt[CLI_TDB_UINT] = 0;
 		    return -1;
 		}
 		tdb->val[cnt] = atoi(pt);
 		break;
 
 	    case CLI_TDB_RANGE:
 		if(!(pt2 = strchr(pt, '-'))) {
 		    cli_errmsg("lsigattribs: Incorrect parameters in '%s'\n", tokens[i]);
 		    return -1;
 		}
 		*pt2++ = 0;
 		off[i] = cnt = tdb->cnt[CLI_TDB_RANGE];
 		tdb->cnt[CLI_TDB_RANGE] += 2;
47d40feb
 		tdb->range = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->range, tdb->cnt[CLI_TDB_RANGE] * sizeof(uint32_t));
677fc4ba
 		if(!tdb->range) {
 		    tdb->cnt[CLI_TDB_RANGE] = 0;
 		    return -1;
 		}
 		tdb->range[cnt] = atoi(pt);
 		tdb->range[cnt + 1] = atoi(pt2);
 		break;
 
 	    case CLI_TDB_RANGE2:
 		if(!strchr(pt, '-') || !strchr(pt, '.')) {
 		    cli_errmsg("lsigattribs: Incorrect parameters in '%s'\n", tokens[i]);
 		    return -1;
 		}
 		off[i] = cnt = tdb->cnt[CLI_TDB_RANGE];
 		tdb->cnt[CLI_TDB_RANGE] += 3;
47d40feb
 		tdb->range = (uint32_t *) mpool_realloc2(tdb->mempool, tdb->range, tdb->cnt[CLI_TDB_RANGE] * sizeof(uint32_t));
677fc4ba
 		if(!tdb->range) {
 		    tdb->cnt[CLI_TDB_RANGE] = 0;
 		    return -1;
 		}
 		if(sscanf(pt, "%u.%u-%u", &v1, &v2, &v3) != 3) {
 		    cli_errmsg("lsigattribs: Can't parse parameters in '%s'\n", tokens[i]);
 		    return -1;
 		}
 		tdb->range[cnt] = (uint32_t) v1;
 		tdb->range[cnt + 1] = (uint32_t) v2;
 		tdb->range[cnt + 2] = (uint32_t) v3;
 		break;
 
 	    case CLI_TDB_STR:
 		off[i] = cnt = tdb->cnt[CLI_TDB_STR];
 		tdb->cnt[CLI_TDB_STR] += strlen(pt) + 1;
47d40feb
 		tdb->str = (char *) mpool_realloc2(tdb->mempool, tdb->str, tdb->cnt[CLI_TDB_STR] * sizeof(char));
677fc4ba
 		if(!tdb->str) {
 		    cli_errmsg("lsigattribs: Can't allocate memory for tdb->str\n");
 		    return -1;
 		}
 		memcpy(&tdb->str[cnt], pt, strlen(pt));
 		tdb->str[tdb->cnt[CLI_TDB_STR] - 1] = 0;
 		break;
 	}
     }
 
     if(!i) {
 	cli_errmsg("lsigattribs: Empty TDB\n");
 	return -1;
     }
 
72fb25ea
     for(i = 0; i < tokens_count; i++) {
677fc4ba
 	for(j = 0; attrtab[j].name; j++) {
 	    if(!strcmp(attrtab[j].name, tokens[i])) {
 		apt = &attrtab[j];
 		break;
 	    }
 	}
c9157be3
 	if(!apt)
 	    continue;
677fc4ba
 	switch(apt->type) {
 	    case CLI_TDB_UINT:
 		*apt->pt = (uint32_t *) &tdb->val[off[i]];
 		break;
 
 	    case CLI_TDB_RANGE:
 	    case CLI_TDB_RANGE2:
 		*apt->pt = (uint32_t *) &tdb->range[off[i]];
 		break;
 
 	    case CLI_TDB_STR:
 		*apt->pt = (char *) &tdb->str[off[i]];
 		break;
 	}
     }
 
     return 0;
 }
 
0d9dbdef
 #define FREE_TDB(x) do {		\
   if(x.cnt[CLI_TDB_UINT])		\
47d40feb
     mpool_free(x.mempool, x.val);		\
0d9dbdef
   if(x.cnt[CLI_TDB_RANGE])		\
47d40feb
     mpool_free(x.mempool, x.range);	\
0d9dbdef
   if(x.cnt[CLI_TDB_STR])		\
47d40feb
     mpool_free(x.mempool, x.str);		\
0d9dbdef
   } while(0);
677fc4ba
 
 #define LDB_TOKENS 67
15850fc6
 static int cli_loadldb(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
677fc4ba
 {
 	char *tokens[LDB_TOKENS];
4367454d
 	char buffer[CLI_DEFAULT_LSIG_BUFSIZE + 1], *pt;
677fc4ba
 	const char *sig, *virname, *offset, *logic;
 	struct cli_matcher *root;
 	unsigned int line = 0, sigs = 0;
 	unsigned short target = 0;
 	struct cli_ac_lsig **newtable, *lsig;
 	uint32_t lsigid[2];
72fb25ea
 	int ret = CL_SUCCESS, i, subsigs, tokens_count;
677fc4ba
 	struct cli_lsig_tdb tdb;
 
 
15850fc6
     if((ret = cli_initroots(engine, options)))
677fc4ba
 	return ret;
 
4367454d
     while(cli_dbgets(buffer, sizeof(buffer), fs, dbio)) {
677fc4ba
 	line++;
 	sigs++;
 	cli_chomp(buffer);
 
72fb25ea
 	tokens_count = cli_strtokenize(buffer, ';', LDB_TOKENS, (const char **) tokens);
 	if(tokens_count < 4) {
677fc4ba
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
72fb25ea
 	virname = tokens[0];
 	logic = tokens[2];
 
15850fc6
 	if(engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)))
 	    if(cli_chkpua(virname, engine->pua_cats, options))
b023c36d
 		continue;
 
15850fc6
 	if(engine->ignored && cli_chkign(engine->ignored, dbname, line, virname))
677fc4ba
 	    continue;
 
ee8bd2fb
 	subsigs = cli_ac_chklsig(logic, logic + strlen(logic), NULL, NULL, NULL, 1);
677fc4ba
 	if(subsigs == -1) {
 	    ret = CL_EMALFDB;
 	    break;
 	}
 	subsigs++;
 
 	if(subsigs > 64) {
 	    cli_errmsg("cli_loadldb: Broken logical expression or too many subsignatures\n");
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	/* TDB */
 	memset(&tdb, 0, sizeof(tdb));
a963dd92
 #ifdef USE_MPOOL
15850fc6
 	tdb.mempool = engine->mempool;
1e2969a4
 #endif
677fc4ba
 
 	if(lsigattribs(tokens[1], &tdb) == -1) {
 	    FREE_TDB(tdb);
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
 	if(tdb.engine) {
 	    if(tdb.engine[0] > cl_retflevel()) {
 		cli_dbgmsg("cli_loadldb: Signature for %s not loaded (required f-level: %u)\n", virname, tdb.engine[0]);
 		FREE_TDB(tdb);
 		sigs--;
 		continue;
 	    } else if(tdb.engine[1] < cl_retflevel()) {
 		FREE_TDB(tdb);
 		sigs--;
 		continue;
 	    }
 	}
 
 	if(!tdb.target) {
 	    cli_errmsg("cli_loadldb: No target specified in TDB\n");
 	    FREE_TDB(tdb);
 	    ret = CL_EMALFDB;
 	    break;
 	} else if(tdb.target[0] >= CLI_MTARGETS) {
 	    cli_dbgmsg("cli_loadldb: Not supported target type in logical signature for %s\n", virname);
 	    FREE_TDB(tdb);
 	    sigs--;
 	    continue;
 	}
 
15850fc6
 	root = engine->root[tdb.target[0]];
677fc4ba
 
47d40feb
 	lsig = (struct cli_ac_lsig *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_ac_lsig));
677fc4ba
 	if(!lsig) {
 	    cli_errmsg("cli_loadldb: Can't allocate memory for lsig\n");
 	    FREE_TDB(tdb);
 	    ret = CL_EMEM;
 	    break;
 	}
d6e1ef16
 
47d40feb
 	lsig->logic = cli_mpool_strdup(engine->mempool, logic);
677fc4ba
 	if(!lsig->logic) {
 	    cli_errmsg("cli_loadldb: Can't allocate memory for lsig->logic\n");
 	    FREE_TDB(tdb);
 	    ret = CL_EMEM;
47d40feb
 	    mpool_free(engine->mempool, lsig);
677fc4ba
 	    break;
 	}
 
 	lsigid[0] = lsig->id = root->ac_lsigs;
 	memcpy(&lsig->tdb, &tdb, sizeof(tdb));
 
 	root->ac_lsigs++;
47d40feb
 	newtable = (struct cli_ac_lsig **) mpool_realloc(engine->mempool, root->ac_lsigtable, root->ac_lsigs * sizeof(struct cli_ac_lsig *));
677fc4ba
 	if(!newtable) {
 	    root->ac_lsigs--;
 	    cli_errmsg("cli_loadldb: Can't realloc root->ac_lsigtable\n");
 	    FREE_TDB(tdb);
47d40feb
 	    mpool_free(engine->mempool, lsig);
677fc4ba
 	    ret = CL_EMEM;
 	    break;
 	}
 	newtable[root->ac_lsigs - 1] = lsig;
 	root->ac_lsigtable = newtable;
 
 	for(i = 0; i < subsigs; i++) {
72fb25ea
 	    if(i >= tokens_count) {
677fc4ba
 		cli_errmsg("cli_loadldb: Missing subsignature id %u\n", i);
 		ret = CL_EMALFDB;
 		break;
 	    }
 	    lsigid[1] = i;
 	    sig = tokens[3 + i];
 
 	    if((pt = strchr(tokens[3 + i], ':'))) {
 		*pt = 0;
 		sig = ++pt;
 		offset = tokens[3 + i];
c7aeeb46
 		if(!strcmp(offset, "*"))
 		    offset = NULL;
677fc4ba
 	    } else {
 		offset = NULL;
 		sig = tokens[3 + i];
 	    }
 
fea1b9bb
 	    if(offset && cli_checkoffset(offset, tdb.target[0])) {
 		cli_errmsg("Incorrect offset '%s' in subsignature id %u for signature type-%u\n", offset, i, tdb.target[0]);
 		ret = CL_EMALFDB;
 		break;
 	    }
 
677fc4ba
 	    if((ret = cli_parse_add(root, virname, sig, 0, 0, offset, target, lsigid, options))) {
 		ret = CL_EMALFDB;
 		break;
 	    }
 	}
 	if(ret)
 	    break;
     }
 
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %u\n", line);
 	return ret;
     }
 
     if(signo)
 	*signo += sigs;
 
     return CL_SUCCESS;
 }
 
72fb25ea
 #define FTM_TOKENS 8
15850fc6
 static int cli_loadftm(FILE *fs, struct cl_engine *engine, unsigned int options, unsigned int internal, struct cli_dbio *dbio)
7021b545
 {
72fb25ea
 	const char *tokens[FTM_TOKENS + 1], *pt;
7021b545
 	char buffer[FILEBUFF];
72fb25ea
 	unsigned int line = 0, sigs = 0, tokens_count;
7021b545
 	struct cli_ftype *new;
6038397e
 	cli_file_t rtype, type;
7021b545
 	int ret;
 
 
15850fc6
     if((ret = cli_initroots(engine, options)))
7021b545
 	return ret;
 
     while(1) {
 	if(internal) {
 	    if(!ftypes_int[line])
 		break;
 	    strncpy(buffer, ftypes_int[line], sizeof(buffer));
72ce4b70
 	    buffer[sizeof(buffer)-1]='\0';
7021b545
 	} else {
e8ae4fae
 	    if(!cli_dbgets(buffer, FILEBUFF, fs, dbio))
7021b545
 		break;
 	    cli_chomp(buffer);
 	}
 	line++;
72fb25ea
 	tokens_count = cli_strtokenize(buffer, ':', FTM_TOKENS + 1, tokens);
7021b545
 
72fb25ea
 	if(tokens_count < 6 || tokens_count > 8) {
7021b545
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
72fb25ea
 	if(tokens_count > 6) { /* min version */
 	    pt = tokens[6];
6038397e
 	    if(!cli_isnumber(pt)) {
 		ret = CL_EMALFDB;
 		break;
 	    }
 	    if((unsigned int) atoi(pt) > cl_retflevel()) {
 		cli_dbgmsg("cli_loadftm: File type signature for %s not loaded (required f-level: %u)\n", tokens[3], atoi(pt));
 		continue;
 	    }
72fb25ea
 	    if(tokens_count == 8) { /* max version */
 		pt = tokens[7];
6038397e
 		if(!cli_isnumber(pt)) {
 		    ret = CL_EMALFDB;
 		    break;
 		}
 		if((unsigned int) atoi(pt) < cl_retflevel())
 		    continue;
 	    }
 	}
 
 	rtype = cli_ftcode(tokens[4]);
 	type = cli_ftcode(tokens[5]);
 	if(rtype == CL_TYPE_ERROR || type == CL_TYPE_ERROR) {
7021b545
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
6038397e
 	if(atoi(tokens[0]) == 1) { /* A-C */
15850fc6
 	    if((ret = cli_parse_add(engine->root[0], tokens[3], tokens[2], rtype, type, strcmp(tokens[1], "*") ? tokens[1] : NULL, 0, NULL, options)))
7021b545
 		break;
 
6038397e
 	} else if(atoi(tokens[0]) == 0) { /* memcmp() */
47d40feb
 	    new = (struct cli_ftype *) mpool_malloc(engine->mempool, sizeof(struct cli_ftype));
7021b545
 	    if(!new) {
 		ret = CL_EMEM;
 		break;
 	    }
 	    new->type = type;
6038397e
 	    new->offset = atoi(tokens[1]);
47d40feb
 	    new->magic = (unsigned char *) cli_mpool_hex2str(engine->mempool, tokens[2]);
7021b545
 	    if(!new->magic) {
6038397e
 		cli_errmsg("cli_loadftm: Can't decode the hex string\n");
7021b545
 		ret = CL_EMALFDB;
47d40feb
 		mpool_free(engine->mempool, new);
7021b545
 		break;
 	    }
6038397e
 	    new->length = strlen(tokens[2]) / 2;
47d40feb
 	    new->tname = cli_mpool_strdup(engine->mempool, tokens[3]);
7021b545
 	    if(!new->tname) {
47d40feb
 		mpool_free(engine->mempool, new->magic);
 		mpool_free(engine->mempool, new);
7021b545
 		ret = CL_EMEM;
 		break;
 	    }
15850fc6
 	    new->next = engine->ftypes;
 	    engine->ftypes = new;
6038397e
 
 	} else {
 	    cli_dbgmsg("cli_loadftm: Unsupported mode %u\n", atoi(tokens[0]));
 	    continue;
7021b545
 	}
6038397e
 	sigs++;
7021b545
     }
 
6038397e
     if(ret) {
 	cli_errmsg("Problem parsing %s filetype database at line %u\n", internal ? "built-in" : "external", line);
 	return ret;
7021b545
     }
 
6038397e
     if(!sigs) {
 	cli_errmsg("Empty %s filetype database\n", internal ? "built-in" : "external");
 	return CL_EMALFDB;
7021b545
     }
 
6038397e
     cli_dbgmsg("Loaded %u filetype definitions\n", sigs);
7021b545
     return CL_SUCCESS;
 }
 
d6e1ef16
 #define IGN_TOKENS 3
15850fc6
 static int cli_loadign(FILE *fs, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio)
ed9753e9
 {
72fb25ea
 	const char *tokens[IGN_TOKENS + 1];
b9b47784
 	char buffer[FILEBUFF];
72fb25ea
 	unsigned int line = 0, tokens_count;
6c0d1684
 	struct cli_ignsig *new;
ac1b219c
 	int ret = CL_SUCCESS;
ed9753e9
 
 
15850fc6
     if(!engine->ignored) {
 	engine->ignored = (struct cli_ignored *) cli_calloc(sizeof(struct cli_ignored), 1);
 	if(!engine->ignored || hashset_init(&engine->ignored->hs, 64, 50))
ed9753e9
 	    return CL_EMEM;
     }
 
e8ae4fae
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
ed9753e9
 	line++;
 	cli_chomp(buffer);
72fb25ea
 	tokens_count = cli_strtokenize(buffer, ':', IGN_TOKENS + 1, tokens);
 	if(tokens_count != IGN_TOKENS) {
 	    ret = CL_EMALFDB;
 	    break;
 	}
d6e1ef16
 
47d40feb
 	new = (struct cli_ignsig *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_ignsig));
ed9753e9
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
 	}
 
47d40feb
 	new->dbname = cli_mpool_strdup(engine->mempool, tokens[0]);
d6e1ef16
 
0728972e
 	if(!new->dbname) {
47d40feb
 	    mpool_free(engine->mempool, new);
ed9753e9
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
d6e1ef16
 	new->line = atoi(tokens[1]);
ed9753e9
 
15850fc6
 	if((ret = hashset_addkey(&engine->ignored->hs, new->line)))
ed9753e9
 	    break;
 
47d40feb
 	new->signame = cli_mpool_strdup(engine->mempool, tokens[2]);
0728972e
 	if(!new->signame) {
47d40feb
 	    mpool_free(engine->mempool, new->dbname);
 	    mpool_free(engine->mempool, new);
ed9753e9
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
15850fc6
 	new->next = engine->ignored->list;
 	engine->ignored->list = new;
ed9753e9
     }
 
     if(ret) {
 	cli_errmsg("cli_loadign: Problem parsing database at line %u\n", line);
 	return ret;
     }
 
     return CL_SUCCESS;
 }
 
 static void cli_freeign(struct cl_engine *engine)
 {
 	struct cli_ignsig *pt;
 	struct cli_ignored *ignored;
 
     if((ignored = engine->ignored)) {
 	while(ignored->list) {
 	    pt = ignored->list;
 	    ignored->list = ignored->list->next;
47d40feb
 	    mpool_free(engine->mempool, pt->dbname);
 	    mpool_free(engine->mempool, pt->signame);
 	    mpool_free(engine->mempool,pt);
ed9753e9
 	}
 	hashset_destroy(&ignored->hs);
 	free(engine->ignored);
 	engine->ignored = NULL;
     }
 }
 
0a3d4094
 static int scomp(const void *a, const void *b)
 {
     return *(const uint32_t *)a - *(const uint32_t *)b;
 }
 
3dad68eb
 #define MD5_HDB	    0
 #define MD5_MDB	    1
 #define MD5_FP	    2
2b459819
 
15850fc6
 static int cli_md5db_init(struct cl_engine *engine, unsigned int mode)
2b459819
 {
 	struct cli_matcher *bm = NULL;
 	int ret;
 
 
     if(mode == MD5_HDB) {
47d40feb
 	bm = engine->md5_hdb = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
2b459819
     } else if(mode == MD5_MDB) {
47d40feb
 	bm = engine->md5_mdb = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
2b459819
     } else {
47d40feb
 	bm = engine->md5_fp = (struct cli_matcher *) mpool_calloc(engine->mempool, sizeof(struct cli_matcher), 1);
2b459819
     }
 
     if(!bm)
 	return CL_EMEM;
0728972e
 #ifdef USE_MPOOL
15850fc6
     bm->mempool = engine->mempool;
0728972e
 #endif
2b459819
     if((ret = cli_bm_init(bm))) {
 	cli_errmsg("cli_md5db_init: Failed to initialize B-M\n");
 	return ret;
     }
 
     return CL_SUCCESS;
 }
 
 #define MD5_DB			    \
     if(mode == MD5_HDB)		    \
15850fc6
 	db = engine->md5_hdb;    \
2b459819
     else if(mode == MD5_MDB)	    \
15850fc6
 	db = engine->md5_mdb;    \
2b459819
     else			    \
15850fc6
 	db = engine->md5_fp;
2b459819
 
a72b7d2e
 #define MD5_TOKENS 3
15850fc6
 static int cli_loadmd5(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int mode, unsigned int options, struct cli_dbio *dbio, const char *dbname)
4048c4f6
 {
72fb25ea
 	const char *tokens[MD5_TOKENS + 1];
a72b7d2e
 	char buffer[FILEBUFF];
 	const char *pt;
0a3d4094
 	int ret = CL_SUCCESS;
72fb25ea
 	unsigned int size_field = 1, md5_field = 0, line = 0, sigs = 0, tokens_count;
2b459819
 	uint32_t size;
 	struct cli_bm_patt *new;
 	struct cli_matcher *db = NULL;
e3aaff8e
 
4048c4f6
 
3dad68eb
     if(mode == MD5_MDB) {
 	size_field = 0;
 	md5_field = 1;
8ce42de3
     }
 
e8ae4fae
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
e3aaff8e
 	line++;
 	cli_chomp(buffer);
72fb25ea
 	tokens_count = cli_strtokenize(buffer, ':', MD5_TOKENS + 1, tokens);
 	if(tokens_count != MD5_TOKENS) {
b023c36d
 	    ret = CL_EMALFDB;
 	    break;
 	}
 
72fb25ea
 	pt = tokens[2]; /* virname */
15850fc6
 	if(engine->pua_cats && (options & CL_DB_PUA_MODE) && (options & (CL_DB_PUA_INCLUDE | CL_DB_PUA_EXCLUDE)))
 	    if(cli_chkpua(pt, engine->pua_cats, options))
b023c36d
 		continue;
 
15850fc6
 	if(engine->ignored && cli_chkign(engine->ignored, dbname, line, pt))
b023c36d
 	    continue;
 
47d40feb
 	new = (struct cli_bm_patt *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_bm_patt));
4048c4f6
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
e3aaff8e
 	}
 
72fb25ea
 	pt = tokens[md5_field]; /* md5 */
47d40feb
 	if(strlen(pt) != 32 || !(new->pattern = (unsigned char *) cli_mpool_hex2str(engine->mempool, pt))) {
0728972e
 	    cli_errmsg("cli_loadmd5: Malformed MD5 string at line %u\n", line);
47d40feb
 	    mpool_free(engine->mempool, new);
0728972e
 	    ret = CL_EMALFDB;
 	    break;
 	}
2b459819
 	new->length = 16;
e3aaff8e
 
72fb25ea
 	size = atoi(tokens[size_field]);
022a21cf
 
47d40feb
 	new->virname = cli_mpool_virname(engine->mempool, (char *) tokens[2], options & CL_DB_OFFICIAL);
0728972e
 	if(!new->virname) {
47d40feb
 	    mpool_free(engine->mempool, new->pattern);
 	    mpool_free(engine->mempool, new);
4048c4f6
 	    ret = CL_EMALFDB;
 	    break;
e3aaff8e
 	}
4048c4f6
 
2b459819
 	MD5_DB;
 	if(!db && (ret = cli_md5db_init(engine, mode))) {
47d40feb
 	    mpool_free(engine->mempool, new->pattern);
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
2b459819
 	    break;
 	} else {
 	    MD5_DB;
 	}
0a3d4094
 
2b459819
 	if((ret = cli_bm_addpatt(db, new))) {
 	    cli_errmsg("cli_loadmd5: Error adding BM pattern\n");
47d40feb
 	    mpool_free(engine->mempool, new->pattern);
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
2b459819
 	    break;
 	}
0a3d4094
 
2b459819
 	if(mode == MD5_MDB) { /* section MD5 */
c3671221
 	    if(!db->md5_sizes_hs.capacity) {
fe389c84
 		    hashset_init(&db->md5_sizes_hs, 65536, 80);
8000d078
 	    }
c3671221
 	    hashset_addkey(&db->md5_sizes_hs, size);
c09d6c19
 	}
ed9753e9
 
 	sigs++;
e3aaff8e
     }
 
4048c4f6
     if(!line) {
3dad68eb
 	cli_errmsg("cli_loadmd5: Empty database file\n");
4048c4f6
 	return CL_EMALFDB;
     }
e3aaff8e
 
4048c4f6
     if(ret) {
3dad68eb
 	cli_errmsg("cli_loadmd5: Problem parsing database at line %u\n", line);
4048c4f6
 	return ret;
     }
ee039e40
 
3805ebcb
     if(signo)
ed9753e9
 	*signo += sigs;
3365db23
 
8d3aca30
     return CL_SUCCESS;
e3aaff8e
 }
 
d6e1ef16
 #define MD_TOKENS 9
15850fc6
 static int cli_loadmd(FILE *fs, struct cl_engine *engine, unsigned int *signo, int type, unsigned int options, struct cli_dbio *dbio, const char *dbname)
e5916a51
 {
72fb25ea
 	const char *tokens[MD_TOKENS + 1];
b9b47784
 	char buffer[FILEBUFF];
72fb25ea
 	unsigned int line = 0, sigs = 0, tokens_count;
ac1b219c
 	int ret = CL_SUCCESS, crc;
a62ae54f
 	struct cli_meta_node *new;
e5916a51
 
 
e8ae4fae
     while(cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
e5916a51
 	line++;
ed9753e9
 	if(buffer[0] == '#')
e5916a51
 	    continue;
 
 	cli_chomp(buffer);
72fb25ea
 	tokens_count = cli_strtokenize(buffer, ':', MD_TOKENS + 1, tokens);
 	if(tokens_count != MD_TOKENS) {
 	    ret = CL_EMALFDB;
 	    break;
 	}
e5916a51
 
47d40feb
 	new = (struct cli_meta_node *) mpool_calloc(engine->mempool, 1, sizeof(struct cli_meta_node));
e5916a51
 	if(!new) {
 	    ret = CL_EMEM;
 	    break;
 	}
 
47d40feb
 	new->virname = cli_mpool_virname(engine->mempool, (char *)tokens[0], options & CL_DB_OFFICIAL);
0728972e
 	if(!new->virname) {
47d40feb
 	    mpool_free(engine->mempool, new);
b5513f8d
 	    ret = CL_EMEM;
e5916a51
 	    break;
 	}
 
15850fc6
 	if(engine->ignored && cli_chkign(engine->ignored, dbname, line, new->virname)) {
47d40feb
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
ed9753e9
 	    continue;
 	}
 
d6e1ef16
 	new->encrypted = atoi(tokens[1]);
47d40feb
 	new->filename = cli_mpool_strdup(engine->mempool, tokens[2]);
0728972e
 	if(!new->filename) {
47d40feb
 	    mpool_free(engine->mempool, new->virname);
 	    mpool_free(engine->mempool, new);
e5916a51
 	    ret = CL_EMALFDB;
 	    break;
 	} else {
 	    if(!strcmp(new->filename, "*")) {
47d40feb
 	        mpool_free(engine->mempool, new->filename);
e5916a51
 		new->filename = NULL;
 	    }
 	}
 
d6e1ef16
 	if(!strcmp(tokens[3], "*"))
 	    new->size = -1;
 	else
 	    new->size = atoi(tokens[3]);
e5916a51
 
d6e1ef16
 	if(!strcmp(tokens[4], "*"))
 	    new->csize = -1;
 	else
 	    new->csize = atoi(tokens[4]);
e5916a51
 
d6e1ef16
 	if(!strcmp(tokens[5], "*")) {
 	    new->crc32 = 0;
e5916a51
 	} else {
d6e1ef16
 	    crc = cli_hex2num(tokens[5]);
 	    if(crc == -1) {
 	        ret = CL_EMALFDB;
 		break;
e5916a51
 	    }
d6e1ef16
 	    new->crc32 = (unsigned int) crc;
e5916a51
 	}
 
d6e1ef16
 	if(!strcmp(tokens[6], "*"))
 	    new->method = -1;
 	else
 	    new->method = atoi(tokens[6]);
e5916a51
 
d6e1ef16
 	if(!strcmp(tokens[7], "*"))
 	    new->fileno = 0;
 	else
 	    new->fileno = atoi(tokens[7]);
19e2ac07
 
d6e1ef16
 	if(!strcmp(tokens[8], "*"))
 	    new->maxdepth = 0;
 	else
 	    new->maxdepth = atoi(tokens[8]);
19e2ac07
 
a62ae54f
 	if(type == 1) {
15850fc6
 	    new->next = engine->zip_mlist;
 	    engine->zip_mlist = new;
a62ae54f
 	} else {
15850fc6
 	    new->next = engine->rar_mlist;
 	    engine->rar_mlist = new;
a62ae54f
 	}
ed9753e9
 
 	sigs++;
e5916a51
     }
 
     if(!line) {
 	cli_errmsg("Empty database file\n");
 	return CL_EMALFDB;
     }
 
     if(ret) {
 	cli_errmsg("Problem parsing database at line %d\n", line);
 	return ret;
     }
 
     if(signo)
ed9753e9
 	*signo += sigs;
e5916a51
 
8d3aca30
     return CL_SUCCESS;
e5916a51
 }
 
15850fc6
 static int cli_loaddbdir(const char *dirname, struct cl_engine *engine, unsigned int *signo, unsigned int options);
52c67b86
 
15850fc6
 int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio)
e3aaff8e
 {
056d95dc
 	FILE *fs = NULL;
f51e962f
 	int ret = CL_SUCCESS;
4efca56b
 	uint8_t skipped = 0;
ed9753e9
 	const char *dbname;
f51e962f
 
4048c4f6
 
e8ae4fae
     if(!dbio && (fs = fopen(filename, "rb")) == NULL) {
f51e962f
 	cli_errmsg("cli_load(): Can't open file %s\n", filename);
4048c4f6
 	return CL_EOPEN;
     }
 
ed9753e9
 /*
 #ifdef C_WINDOWS
     if((dbname = strrchr(filename, '\\')))
 #else
 */
     if((dbname = strrchr(filename, '/')))
 /*#endif */
 	dbname++;
     else
 	dbname = filename;
4048c4f6
 
ed9753e9
     if(cli_strbcasestr(dbname, ".db")) {
e8ae4fae
 	ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
ed9753e9
 
     } else if(cli_strbcasestr(dbname, ".cvd")) {
ac1b219c
 	ret = cli_cvdload(fs, engine, signo, !strcmp(dbname, "daily.cvd"), options, 0);
9d193ff2
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".cld")) {
ac1b219c
 	ret = cli_cvdload(fs, engine, signo, !strcmp(dbname, "daily.cld"), options | CL_DB_CVDNOTMP, 1);
4048c4f6
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".hdb")) {
e8ae4fae
 	ret = cli_loadmd5(fs, engine, signo, MD5_HDB, options, dbio, dbname);
db65451b
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".hdu")) {
70edb085
 	if(options & CL_DB_PUA)
b023c36d
 	    ret = cli_loadmd5(fs, engine, signo, MD5_HDB, options | CL_DB_PUA_MODE, dbio, dbname);
70edb085
 	else
 	    skipped = 1;
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".fp")) {
e8ae4fae
 	ret = cli_loadmd5(fs, engine, signo, MD5_FP, options, dbio, dbname);
4048c4f6
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".mdb")) {
e8ae4fae
 	ret = cli_loadmd5(fs, engine, signo, MD5_MDB, options, dbio, dbname);
c09d6c19
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".mdu")) {
70edb085
 	if(options & CL_DB_PUA)
b023c36d
 	    ret = cli_loadmd5(fs, engine, signo, MD5_MDB, options | CL_DB_PUA_MODE, dbio, dbname);
70edb085
 	else
 	    skipped = 1;
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".ndb")) {
e8ae4fae
 	ret = cli_loadndb(fs, engine, signo, 0, options, dbio, dbname);
555c5390
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".ndu")) {
09eab32a
 	if(!(options & CL_DB_PUA))
70edb085
 	    skipped = 1;
 	else
b023c36d
 	    ret = cli_loadndb(fs, engine, signo, 0, options | CL_DB_PUA_MODE, dbio, dbname);
70edb085
 
677fc4ba
     } else if(cli_strbcasestr(filename, ".ldb")) {
        ret = cli_loadldb(fs, engine, signo, options, dbio, dbname);
 
     } else if(cli_strbcasestr(filename, ".ldu")) {
 	if(options & CL_DB_PUA)
b023c36d
 	    ret = cli_loadldb(fs, engine, signo, options | CL_DB_PUA_MODE, dbio, dbname);
677fc4ba
 	else
 	    skipped = 1;
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".sdb")) {
e8ae4fae
 	ret = cli_loadndb(fs, engine, signo, 1, options, dbio, dbname);
b68d11d2
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".zmd")) {
e8ae4fae
 	ret = cli_loadmd(fs, engine, signo, 1, options, dbio, dbname);
a62ae54f
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".rmd")) {
e8ae4fae
 	ret = cli_loadmd(fs, engine, signo, 2, options, dbio, dbname);
e5916a51
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".cfg")) {
e8ae4fae
 	ret = cli_dconf_load(fs, engine, options, dbio);
bc93eda0
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".wdb")) {
056d95dc
 	if(options & CL_DB_PHISHING_URLS) {
e8ae4fae
 	    ret = cli_loadwdb(fs, engine, options, dbio);
056d95dc
 	} else
7dec7955
 	    skipped = 1;
0810d861
     } else if(cli_strbcasestr(dbname, ".pdb") || cli_strbcasestr(dbname, ".gdb")) {
056d95dc
 	if(options & CL_DB_PHISHING_URLS) {
03527bee
 	    ret = cli_loadpdb(fs, engine, signo, options, dbio);
056d95dc
 	} else
7dec7955
 	    skipped = 1;
6038397e
     } else if(cli_strbcasestr(dbname, ".ftm")) {
e8ae4fae
 	ret = cli_loadftm(fs, engine, options, 0, dbio);
7021b545
 
ed9753e9
     } else if(cli_strbcasestr(dbname, ".ign")) {
e8ae4fae
 	ret = cli_loadign(fs, engine, options, dbio);
ed9753e9
 
4048c4f6
     } else {
f51e962f
 	cli_dbgmsg("cli_load: unknown extension - assuming old database format\n");
e8ae4fae
 	ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
4048c4f6
     }
 
4efca56b
     if(ret) {
975ef8ce
 	cli_errmsg("Can't load %s: %s\n", filename, cl_strerror(ret));
4efca56b
     } else  {
 	if(skipped)
 	    cli_dbgmsg("%s skipped\n", filename);
 	else
 	    cli_dbgmsg("%s loaded\n", filename);
     }
4048c4f6
 
056d95dc
     if(fs)
 	fclose(fs);
 
4048c4f6
     return ret;
e3aaff8e
 }
 
15850fc6
 static int cli_loaddbdir(const char *dirname, struct cl_engine *engine, unsigned int *signo, unsigned int options)
e3aaff8e
 {
 	DIR *dd;
 	struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
 	char *dbfile;
871177cd
 	int ret = CL_EOPEN;
e3aaff8e
 
 
0eacacec
     cli_dbgmsg("Loading databases from %s\n", dirname);
ed9753e9
     dbfile = (char *) cli_malloc(strlen(dirname) + 20);
0eacacec
     if(!dbfile)
 	return CL_EMEM;
ed9753e9
 
     /* try to load local.ign and daily.cvd/daily.ign first */
     sprintf(dbfile, "%s/local.ign", dirname);
e8ae4fae
     if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
ed9753e9
 	free(dbfile);
 	return ret;
     }
 
     sprintf(dbfile, "%s/daily.cld", dirname);
     if(access(dbfile, R_OK))
 	sprintf(dbfile, "%s/daily.cvd", dirname);
e8ae4fae
     if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
ed9753e9
 	free(dbfile);
 	return ret;
     }
 
     sprintf(dbfile, "%s/daily.ign", dirname);
e8ae4fae
     if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
ed9753e9
 	free(dbfile);
 	return ret;
     }
 
816d66a8
     /* try to load local.gdb next */
     sprintf(dbfile, "%s/local.gdb", dirname);
     if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
 	free(dbfile);
 	return ret;
     }
 
ed9753e9
     /* check for and load daily.cfg */
0eacacec
     sprintf(dbfile, "%s/daily.cfg", dirname);
e8ae4fae
     if(!access(dbfile, R_OK) && (ret = cli_load(dbfile, engine, signo, options, NULL))) {
ed9753e9
 	free(dbfile);
 	return ret;
0eacacec
     }
     free(dbfile);
 
e3aaff8e
     if((dd = opendir(dirname)) == NULL) {
15e1a7cd
         cli_errmsg("cli_loaddbdir(): Can't open directory %s\n", dirname);
e3aaff8e
         return CL_EOPEN;
     }
 
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
     while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
     while((dent = readdir(dd))) {
72a1b240
 #endif
6670d61d
 #if	(!defined(C_INTERIX)) && (!defined(C_WINDOWS))
feeaa333
 	if(dent->d_ino)
 #endif
 	{
ed9753e9
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && strcmp(dent->d_name, "daily.cvd") && strcmp(dent->d_name, "daily.cld") && strcmp(dent->d_name, "daily.ign") && strcmp(dent->d_name, "daily.cfg") && strcmp(dent->d_name, "local.ign") && CLI_DBEXT(dent->d_name)) {
8139fd99
 
0eacacec
 		dbfile = (char *) cli_malloc(strlen(dent->d_name) + strlen(dirname) + 2);
e3aaff8e
 
 		if(!dbfile) {
15e1a7cd
 		    cli_dbgmsg("cli_loaddbdir(): dbfile == NULL\n");
e3aaff8e
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 		sprintf(dbfile, "%s/%s", dirname, dent->d_name);
e8ae4fae
 		ret = cli_load(dbfile, engine, signo, options, NULL);
3dad68eb
 
 		if(ret) {
15e1a7cd
 		    cli_dbgmsg("cli_loaddbdir(): error loading database %s\n", dbfile);
e3aaff8e
 		    free(dbfile);
 		    closedir(dd);
 		    return ret;
 		}
 		free(dbfile);
 	    }
 	}
     }
 
     closedir(dd);
871177cd
     if(ret == CL_EOPEN)
d6898042
 	cli_errmsg("cli_loaddb(): No supported database files found in %s\n", dirname);
 
     return ret;
e3aaff8e
 }
 
2accc66f
 int cl_load(const char *path, struct cl_engine *engine, unsigned int *signo, unsigned int dboptions)
04933acd
 {
 	struct stat sb;
f7470773
 	int ret;
04933acd
 
15850fc6
     if(!engine) {
724b2bf7
 	cli_errmsg("cl_load: engine == NULL\n");
 	return CL_ENULLARG;
     }
04933acd
 
d7c3f6e2
     if(engine->dboptions & CL_DB_COMPILED) {
 	cli_errmsg("cl_load(): can't load new databases when engine is already compiled\n");
 	return CL_EARG;
     }
 
04933acd
     if(stat(path, &sb) == -1) {
ac1b219c
         cli_errmsg("cl_load(): Can't get status of %s\n", path);
871177cd
         return CL_ESTAT;
04933acd
     }
 
2accc66f
     if((dboptions & CL_DB_PHISHING_URLS) && !engine->phishcheck && (engine->dconf->phishing & PHISHING_CONF_ENGINE))
15850fc6
 	if((ret = phishing_init(engine)))
724b2bf7
 	    return ret;
f7470773
 
d7c3f6e2
     engine->dboptions |= dboptions;
616bc3b9
 
f51e962f
     switch(sb.st_mode & S_IFMT) {
04933acd
 	case S_IFREG: 
2accc66f
 	    ret = cli_load(path, engine, signo, dboptions, NULL);
bc93eda0
 	    break;
04933acd
 
 	case S_IFDIR:
2accc66f
 	    ret = cli_loaddbdir(path, engine, signo, dboptions);
bc93eda0
 	    break;
04933acd
 
 	default:
bc93eda0
 	    cli_errmsg("cl_load(%s): Not supported database file type\n", path);
04933acd
 	    return CL_EOPEN;
     }
bc93eda0
     return ret;
04933acd
 }
 
4048c4f6
 const char *cl_retdbdir(void)
 {
     return DATADIR;
 }
 
e3aaff8e
 int cl_statinidir(const char *dirname, struct cl_stat *dbstat)
 {
 	DIR *dd;
2bb229f6
 	const struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
         char *fname;
 
 
     if(dbstat) {
b5134815
 	dbstat->entries = 0;
e3aaff8e
 	dbstat->stattab = NULL;
75ccac9f
 	dbstat->statdname = NULL;
6f38c939
 	dbstat->dir = cli_strdup(dirname);
e3aaff8e
     } else {
         cli_errmsg("cl_statdbdir(): Null argument passed.\n");
 	return CL_ENULLARG;
     }
 
     if((dd = opendir(dirname)) == NULL) {
         cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dirname);
cd69cf20
 	cl_statfree(dbstat);
e3aaff8e
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Stat()ing files in %s\n", dirname);
 
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
     while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
     while((dent = readdir(dd))) {
72a1b240
 #endif
6670d61d
 #if	(!defined(C_INTERIX)) && (!defined(C_WINDOWS))
feeaa333
 	if(dent->d_ino)
 #endif
 	{
3dad68eb
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) {
b5134815
 		dbstat->entries++;
84fd5a61
 		dbstat->stattab = (struct stat *) cli_realloc2(dbstat->stattab, dbstat->entries * sizeof(struct stat));
cd69cf20
 		if(!dbstat->stattab) {
 		    cl_statfree(dbstat);
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 
e871e527
 #if defined(C_INTERIX) || defined(C_OS2)
84fd5a61
 		dbstat->statdname = (char **) cli_realloc2(dbstat->statdname, dbstat->entries * sizeof(char *));
cd69cf20
 		if(!dbstat->statdname) {
 		    cl_statfree(dbstat);
 		    closedir(dd);
 		    return CL_EMEM;
 		}
75ccac9f
 #endif
 
0eacacec
                 fname = cli_malloc(strlen(dirname) + strlen(dent->d_name) + 32);
cd69cf20
 		if(!fname) {
 		    cl_statfree(dbstat);
 		    closedir(dd);
 		    return CL_EMEM;
 		}
9d193ff2
 		sprintf(fname, "%s/%s", dirname, dent->d_name);
e871e527
 #if defined(C_INTERIX) || defined(C_OS2)
0eacacec
 		dbstat->statdname[dbstat->entries - 1] = (char *) cli_malloc(strlen(dent->d_name) + 1);
cd69cf20
 		if(!dbstat->statdname[dbstat->entries - 1]) {
 		    cl_statfree(dbstat);
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 
b5134815
 		strcpy(dbstat->statdname[dbstat->entries - 1], dent->d_name);
75ccac9f
 #endif
b5134815
 		stat(fname, &dbstat->stattab[dbstat->entries - 1]);
e3aaff8e
 		free(fname);
 	    }
 	}
     }
 
     closedir(dd);
8d3aca30
     return CL_SUCCESS;
e3aaff8e
 }
 
 int cl_statchkdir(const struct cl_stat *dbstat)
 {
 	DIR *dd;
 	struct dirent *dent;
72a1b240
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
88794204
 	union {
 	    struct dirent d;
 	    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 	} result;
2bb229f6
 #endif
e3aaff8e
 	struct stat sb;
53721687
 	unsigned int i, found;
e3aaff8e
 	char *fname;
 
 
     if(!dbstat || !dbstat->dir) {
         cli_errmsg("cl_statdbdir(): Null argument passed.\n");
 	return CL_ENULLARG;
     }
 
     if((dd = opendir(dbstat->dir)) == NULL) {
         cli_errmsg("cl_statdbdir(): Can't open directory %s\n", dbstat->dir);
         return CL_EOPEN;
     }
 
     cli_dbgmsg("Stat()ing files in %s\n", dbstat->dir);
 
72a1b240
 #ifdef HAVE_READDIR_R_3
88794204
     while(!readdir_r(dd, &result.d, &dent) && dent) {
72a1b240
 #elif defined(HAVE_READDIR_R_2)
88794204
     while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
72a1b240
 #else
e3aaff8e
     while((dent = readdir(dd))) {
72a1b240
 #endif
6670d61d
 #if	(!defined(C_INTERIX)) && (!defined(C_WINDOWS))
feeaa333
 	if(dent->d_ino)
 #endif
 	{
3dad68eb
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && CLI_DBEXT(dent->d_name)) {
0eacacec
                 fname = cli_malloc(strlen(dbstat->dir) + strlen(dent->d_name) + 32);
cd69cf20
 		if(!fname) {
 		    closedir(dd);
 		    return CL_EMEM;
 		}
 
9d193ff2
 		sprintf(fname, "%s/%s", dbstat->dir, dent->d_name);
e3aaff8e
 		stat(fname, &sb);
 		free(fname);
 
 		found = 0;
b5134815
 		for(i = 0; i < dbstat->entries; i++)
e871e527
 #if defined(C_INTERIX) || defined(C_OS2)
75ccac9f
 		    if(!strcmp(dbstat->statdname[i], dent->d_name)) {
 #else
e3aaff8e
 		    if(dbstat->stattab[i].st_ino == sb.st_ino) {
75ccac9f
 #endif
e3aaff8e
 			found = 1;
e3f0de57
 			if(dbstat->stattab[i].st_mtime != sb.st_mtime) {
 			    closedir(dd);
e3aaff8e
 			    return 1;
e3f0de57
 			}
e3aaff8e
 		    }
 
e3f0de57
 		if(!found) {
 		    closedir(dd);
e3aaff8e
 		    return 1;
e3f0de57
 		}
e3aaff8e
 	    }
 	}
     }
 
     closedir(dd);
8d3aca30
     return CL_SUCCESS;
e3aaff8e
 }
 
 int cl_statfree(struct cl_stat *dbstat)
 {
 
     if(dbstat) {
75ccac9f
 
e871e527
 #if defined(C_INTERIX) || defined(C_OS2)
75ccac9f
 	    int i;
 
cd69cf20
 	if(dbstat->statdname) {
 	    for(i = 0; i < dbstat->entries; i++) {
 		if(dbstat->statdname[i])
 		    free(dbstat->statdname[i]);
 		dbstat->statdname[i] = NULL;
 	    }
 	    free(dbstat->statdname);
 	    dbstat->statdname = NULL;
75ccac9f
 	}
 #endif
 
cd69cf20
 	if(dbstat->stattab) {
 	    free(dbstat->stattab);
 	    dbstat->stattab = NULL;
 	}
b5134815
 	dbstat->entries = 0;
cd69cf20
 
9e431a95
 	if(dbstat->dir) {
e3aaff8e
 	    free(dbstat->dir);
9e431a95
 	    dbstat->dir = NULL;
 	}
e3aaff8e
     } else {
8000d078
         cli_errmsg("cl_statfree(): Null argument passed\n");
e3aaff8e
 	return CL_ENULLARG;
     }
 
8d3aca30
     return CL_SUCCESS;
e3aaff8e
 }
f51e962f
 
724b2bf7
 int cl_engine_free(struct cl_engine *engine)
f51e962f
 {
677fc4ba
 	unsigned int i, j;
f51e962f
 	struct cli_meta_node *metapt, *metah;
 	struct cli_matcher *root;
c9c463fe
 
f51e962f
 
     if(!engine) {
 	cli_errmsg("cl_free: engine == NULL\n");
724b2bf7
 	return CL_ENULLARG;
f51e962f
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&cli_ref_mutex);
 #endif
 
04ba76d2
     if(engine->refcount)
 	engine->refcount--;
 
f51e962f
     if(engine->refcount) {
 #ifdef CL_THREAD_SAFE
 	pthread_mutex_unlock(&cli_ref_mutex);
 #endif
724b2bf7
 	return CL_SUCCESS;
f51e962f
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&cli_ref_mutex);
 #endif
bc93eda0
     if(engine->root) {
4addba22
 	for(i = 0; i < CLI_MTARGETS; i++) {
bc93eda0
 	    if((root = engine->root[i])) {
bedc58de
 		if(!root->ac_only)
bc93eda0
 		    cli_bm_free(root);
bedc58de
 		cli_ac_free(root);
677fc4ba
 		if(root->ac_lsigtable) {
 		    for(j = 0; j < root->ac_lsigs; j++) {
47d40feb
 			mpool_free(engine->mempool, root->ac_lsigtable[j]->logic);
677fc4ba
 			FREE_TDB(root->ac_lsigtable[j]->tdb);
47d40feb
 			mpool_free(engine->mempool, root->ac_lsigtable[j]);
677fc4ba
 		    }
47d40feb
 		    mpool_free(engine->mempool, root->ac_lsigtable);
677fc4ba
 		}
47d40feb
 		mpool_free(engine->mempool, root);
bc93eda0
 	    }
f51e962f
 	}
47d40feb
 	mpool_free(engine->mempool, engine->root);
f51e962f
     }
 
2b459819
     if((root = engine->md5_hdb)) {
 	cli_bm_free(root);
47d40feb
 	mpool_free(engine->mempool, root);
f51e962f
     }
 
2b459819
     if((root = engine->md5_mdb)) {
0a3d4094
 	cli_bm_free(root);
47d40feb
 	mpool_free(engine->mempool, root->soff);
c3671221
 	if(root->md5_sizes_hs.capacity) {
 		hashset_destroy(&root->md5_sizes_hs);
 	}
47d40feb
 	mpool_free(engine->mempool, root);
2f942502
     }
 
2b459819
     if((root = engine->md5_fp)) {
 	cli_bm_free(root);
47d40feb
 	mpool_free(engine->mempool, root);
2b459819
     }
 
f51e962f
     metapt = engine->zip_mlist;
     while(metapt) {
 	metah = metapt;
 	metapt = metapt->next;
47d40feb
 	mpool_free(engine->mempool, metah->virname);
f51e962f
 	if(metah->filename)
47d40feb
 	    mpool_free(engine->mempool, metah->filename);
 	mpool_free(engine->mempool, metah);
f51e962f
     }
 
     metapt = engine->rar_mlist;
     while(metapt) {
 	metah = metapt;
 	metapt = metapt->next;
47d40feb
 	mpool_free(engine->mempool, metah->virname);
f51e962f
 	if(metah->filename)
47d40feb
 	    mpool_free(engine->mempool, metah->filename);
 	mpool_free(engine->mempool, metah);
f51e962f
     }
 
15850fc6
     if(engine->dconf->phishing & PHISHING_CONF_ENGINE)
692bda68
 	phishing_done(engine);
bc93eda0
     if(engine->dconf)
47d40feb
 	mpool_free(engine->mempool, engine->dconf);
bc93eda0
 
b023c36d
     if(engine->pua_cats)
47d40feb
 	mpool_free(engine->mempool, engine->pua_cats);
b023c36d
 
33068e09
     if(engine->tmpdir)
47d40feb
 	mpool_free(engine->mempool, engine->tmpdir);
33068e09
 
0d9dbdef
     cli_ftfree(engine);
ed9753e9
     cli_freeign(engine);
0d9dbdef
 #ifdef USE_MPOOL
47d40feb
     if(engine->mempool) mpool_destroy(engine->mempool);
e21657df
 #endif
f51e962f
     free(engine);
724b2bf7
     return CL_SUCCESS;
f51e962f
 }
 
c3671221
 static void cli_md5db_build(struct cli_matcher* root)
 {
 	if(root && root->md5_sizes_hs.capacity) {
 		/* TODO: use hashset directly, instead of the array when matching*/
a1c9ad2c
 		cli_dbgmsg("Converting hashset to array: %u entries\n", root->md5_sizes_hs.count);
d22176ef
 
 #ifdef USE_MPOOL
 		{
 		uint32_t *mpoolht;
 		unsigned int mpoolhtsz = root->md5_sizes_hs.count * sizeof(*mpoolht);
47d40feb
 		root->soff = mpool_malloc(root->mempool, mpoolhtsz);
d22176ef
 		root->soff_len = hashset_toarray(&root->md5_sizes_hs, &mpoolht);
 		memcpy(root->soff, mpoolht, mpoolhtsz);
 		free(mpoolht);
 		}
 #else
c3671221
 		root->soff_len = hashset_toarray(&root->md5_sizes_hs, &root->soff);
d22176ef
 #endif
c3671221
 		hashset_destroy(&root->md5_sizes_hs);
 		qsort(root->soff, root->soff_len, sizeof(uint32_t), scomp);
 	}
 }
 
724b2bf7
 int cl_engine_compile(struct cl_engine *engine)
f51e962f
 {
7021b545
 	unsigned int i;
 	int ret;
f51e962f
 	struct cli_matcher *root;
 
 
7021b545
     if(!engine)
 	return CL_ENULLARG;
 
     if(!engine->ftypes)
15850fc6
 	if((ret = cli_loadftm(NULL, engine, 0, 1, NULL)))
7021b545
 	    return ret;
f51e962f
 
4addba22
     for(i = 0; i < CLI_MTARGETS; i++) {
 	if((root = engine->root[i])) {
 	    if((ret = cli_ac_buildtrie(root)))
 		return ret;
 	    cli_dbgmsg("matcher[%u]: %s: AC sigs: %u BM sigs: %u %s\n", i, cli_mtargets[i].name, root->ac_patterns, root->bm_patterns, root->ac_only ? "(ac_only mode)" : "");
 	}
     }
f51e962f
 
2e11bcdf
     if((ret = cli_build_regex_list(engine->whitelist_matcher))) {
 	    return ret;
     }
     if((ret = cli_build_regex_list(engine->domainlist_matcher))) {
 	    return ret;
     }
c3671221
     cli_md5db_build(engine->md5_mdb);
ed9753e9
     cli_freeign(engine);
02ce73de
     cli_dconf_print(engine->dconf);
47d40feb
     mpool_flush(engine->mempool);
02ce73de
 
b3df93db
     engine->dboptions |= CL_DB_COMPILED;
8d3aca30
     return CL_SUCCESS;
f51e962f
 }
 
b8fe70b3
 int cl_engine_addref(struct cl_engine *engine)
f51e962f
 {
     if(!engine) {
b8fe70b3
 	cli_errmsg("cl_engine_addref: engine == NULL\n");
 	return CL_ENULLARG;
f51e962f
     }
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_lock(&cli_ref_mutex);
 #endif
 
     engine->refcount++;
 
 #ifdef CL_THREAD_SAFE
     pthread_mutex_unlock(&cli_ref_mutex);
 #endif
 
b8fe70b3
     return CL_SUCCESS;
f51e962f
 }