Browse code

[WIP] added nocase support to clamav ac algorithm

Kevin Lin authored on 2015/02/06 13:52:18
Showing 9 changed files
... ...
@@ -111,7 +111,10 @@ int cli_ac_addpatt(struct cli_matcher *root, struct cli_ac_patt *pattern)
111 111
             }
112 112
         }
113 113
 
114
-        next = pt->trans[(unsigned char) (pattern->pattern[i] & 0xff)]; 
114
+        if (root->ac_nocase)
115
+            next = pt->trans[cli_nocase((unsigned char) (pattern->pattern[i] & 0xff))];
116
+        else
117
+            next = pt->trans[(unsigned char) (pattern->pattern[i] & 0xff)];
115 118
 
116 119
         if(!next) {
117 120
             next = (struct cli_ac_node *) mpool_calloc(root->mempool, 1, sizeof(struct cli_ac_node));
... ...
@@ -762,14 +765,18 @@ int cli_ac_chklsig(const char *expr, const char *end, uint32_t *lsigcnt, unsigne
762 762
  *        an alternative contains strings of different lengths and 
763 763
  *        more than one of them can match at the current position.
764 764
  */
765
-
766 765
 #define AC_MATCH_CHAR(p,b)								\
767
-    switch(wc = p & CLI_MATCH_WILDCARD) {						\
766
+    switch(wc = p & CLI_MATCH_METADATA) {						\
768 767
 	case CLI_MATCH_CHAR:								\
769 768
 	    if((unsigned char) p != b)							\
770 769
 		match = 0;								\
771 770
 	    break;									\
772 771
 											\
772
+	case CLI_MATCH_NOCASE:								\
773
+	    if(cli_nocase((unsigned char)(p & 0xff)) != cli_nocase(b))			\
774
+		match = 0;								\
775
+	    break;									\
776
+											\
773 777
 	case CLI_MATCH_IGNORE:								\
774 778
 	    break;									\
775 779
 											\
... ...
@@ -1250,7 +1257,10 @@ int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **v
1250 1250
     current = root->ac_root;
1251 1251
 
1252 1252
     for(i = 0; i < length; i++)  {
1253
-        current = current->trans[buffer[i]];
1253
+        if (root->ac_nocase)
1254
+            current = current->trans[cli_nocase(buffer[i])];
1255
+        else
1256
+            current = current->trans[buffer[i]];
1254 1257
 
1255 1258
         if(UNLIKELY(IS_FINAL(current))) {
1256 1259
             struct cli_ac_patt *faillist = current->fail->list;
... ...
@@ -1530,12 +1540,12 @@ static int qcompare(const void *a, const void *b)
1530 1530
 }
1531 1531
 
1532 1532
 /* FIXME: clean up the code */
1533
-int cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hexsig, uint32_t sigid, uint16_t parts, uint16_t partno, uint16_t rtype, uint16_t type, uint32_t mindist, uint32_t maxdist, const char *offset, const uint32_t *lsigid, unsigned int options)
1533
+int cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hexsig, const char *sigopts, uint32_t sigid, uint16_t parts, uint16_t partno, uint16_t rtype, uint16_t type, uint32_t mindist, uint32_t maxdist, const char *offset, const uint32_t *lsigid, unsigned int options)
1534 1534
 {
1535 1535
     struct cli_ac_patt *new;
1536 1536
     char *pt, *pt2, *hex = NULL, *hexcpy = NULL;
1537 1537
     uint16_t i, j, ppos = 0, pend, *dec, nzpos = 0;
1538
-    uint8_t wprefix = 0, zprefix = 1, plen = 0, nzplen = 0;
1538
+    uint8_t wprefix = 0, zprefix = 1, plen = 0, nzplen = 0, nocase = 0;
1539 1539
     struct cli_ac_special *newspecial, *specialpt, **newtable;
1540 1540
     int ret, error = CL_SUCCESS;
1541 1541
 
... ...
@@ -1550,6 +1560,22 @@ int cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hex
1550 1550
         return CL_EMALFDB;
1551 1551
     }
1552 1552
 
1553
+    if (sigopts) {
1554
+	i = 0;
1555
+	while (sigopts[i] != '\0') {
1556
+	    switch (sigopts[i]) {
1557
+	    case 'i':
1558
+		nocase = 1;
1559
+		break;
1560
+	    default:
1561
+		cli_errmsg("cli_ac_addsig: Signature for %s uses invalid option: %02x\n", virname, sigopts[i]);
1562
+		return CL_EMALFDB;
1563
+	    }
1564
+
1565
+	    i++;
1566
+	}
1567
+    }
1568
+
1553 1569
     if((new = (struct cli_ac_patt *) mpool_calloc(root->mempool, 1, sizeof(struct cli_ac_patt))) == NULL)
1554 1570
         return CL_EMEM;
1555 1571
 
... ...
@@ -1853,6 +1879,13 @@ int cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hex
1853 1853
     new->length = strlen(hex ? hex : hexsig) / 2;
1854 1854
     free(hex);
1855 1855
 
1856
+    /* setting nocase match */
1857
+    if (nocase) {
1858
+	for (i = 0; i < new->length; ++i)
1859
+	    if ((new->pattern[i] & CLI_MATCH_METADATA) == CLI_MATCH_CHAR)
1860
+		new->pattern[i] += CLI_MATCH_NOCASE;
1861
+    }
1862
+
1856 1863
     if (root->filter) {
1857 1864
         /* so that we can show meaningful messages */
1858 1865
         new->virname = (char*)virname;
... ...
@@ -99,6 +99,6 @@ int cli_ac_buildtrie(struct cli_matcher *root);
99 99
 int cli_ac_init(struct cli_matcher *root, uint8_t mindepth, uint8_t maxdepth, uint8_t dconf_prefiltering);
100 100
 int cli_ac_caloff(const struct cli_matcher *root, struct cli_ac_data *data, const struct cli_target_info *info);
101 101
 void cli_ac_free(struct cli_matcher *root);
102
-int cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hexsig, uint32_t sigid, uint16_t parts, uint16_t partno, uint16_t rtype, uint16_t type, uint32_t mindist, uint32_t maxdist, const char *offset, const uint32_t *lsigid, unsigned int options);
102
+int cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hexsig, const char *sigopts, uint32_t sigid, uint16_t parts, uint16_t partno, uint16_t rtype, uint16_t type, uint32_t mindist, uint32_t maxdist, const char *offset, const uint32_t *lsigid, unsigned int options);
103 103
 
104 104
 #endif
... ...
@@ -43,8 +43,10 @@ struct cli_target_info {
43 43
 #include "fmap.h"
44 44
 #include "mpool.h"
45 45
 
46
-#define CLI_MATCH_WILDCARD	0xff00
46
+#define CLI_MATCH_METADATA	0xff00
47
+#define CLI_MATCH_WILDCARD	0x0f00
47 48
 #define CLI_MATCH_CHAR		0x0000
49
+#define CLI_MATCH_NOCASE	0x1000
48 50
 #define CLI_MATCH_IGNORE	0x0100
49 51
 #define CLI_MATCH_SPECIAL	0x0200
50 52
 #define CLI_MATCH_NIBBLE_HIGH	0x0300
... ...
@@ -108,7 +110,7 @@ struct cli_matcher {
108 108
     struct filter *filter;
109 109
 
110 110
     uint16_t maxpatlen;
111
-    uint8_t ac_only;
111
+    uint8_t ac_nocase, ac_only;
112 112
 
113 113
     /* Perl-Compiled Regular Expressions */
114 114
 #if HAVE_PCRE
... ...
@@ -262,6 +262,9 @@ struct cl_engine {
262 262
     /* Roots table */
263 263
     struct cli_matcher **root;
264 264
 
265
+    /* Yara table */
266
+    struct cli_matcher *yroot;
267
+
265 268
     /* hash matcher for standard MD5 sigs */
266 269
     struct cli_matcher *hm_hdb;
267 270
     /* hash matcher for MD5 sigs for PE sections */
... ...
@@ -116,7 +116,7 @@ char *cli_virname(const char *virname, unsigned int official)
116 116
 }
117 117
 
118 118
 #define PCRE_TOKENS 4
119
-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)
119
+int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hexsig, const char *sigopts, uint16_t rtype, uint16_t type, const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options)
120 120
 {
121 121
     struct cli_bm_patt *bm_new;
122 122
     char *pt, *hexcpy, *start, *n, l, r;
... ...
@@ -178,18 +178,9 @@ int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hex
178 178
 
179 179
         return CL_SUCCESS;
180 180
     }
181
-    if (strchr(hexsig, '/')) {
182
-#if HAVE_PCRE
183
-        /* expected format => ^offset:trigger/regex/[cflags]$ */
184
-	const char *trigger, *pattern, *cflags;
181
+    if (strrchr(hexsig, '/')) {
185 182
         char *start, *end;
186 183
 
187
-        /* get checked */
188
-        if (hexsig[0] == '/') {
189
-            cli_errmsg("cli_parseadd(): PCRE subsig must contain logical trigger\n");
190
-            return CL_EMALFDB;
191
-        }
192
-
193 184
         /* get copied */
194 185
         hexcpy = cli_calloc(hexlen+1, sizeof(char));
195 186
         if(!hexcpy)
... ...
@@ -199,31 +190,48 @@ int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hex
199 199
         /* get delimiters-ed */
200 200
         start = strchr(hexcpy, '/');
201 201
         end = strrchr(hexcpy, '/');
202
-        if (start == end) {
203
-            cli_errmsg("cli_parseadd(): PCRE expression must be delimited by '/'\n");
204
-            free(hexcpy);
205
-            return CL_EMALFDB;
206
-        }
207 202
 
208
-        /* get NULL-ed */
209
-        *start = '\0';
210
-        *end = '\0';
203
+        /* get pcre-ed */
204
+        if (start != end) {
205
+#if HAVE_PCRE
206
+            /* expected format => ^offset:trigger/regex/[cflags]$ */
207
+            const char *trigger, *pattern, *cflags;
211 208
 
212
-        /* get tokens-ed */
213
-        trigger = hexcpy;
214
-        pattern = start+1;
215
-        cflags = end+1;
216
-        if (*cflags == '\0') /* get compat-ed */
217
-            cflags = NULL;
209
+            /* get checked */
210
+            if (hexsig[0] == '/') {
211
+                cli_errmsg("cli_parseadd(): PCRE subsig must contain logical trigger\n");
212
+                return CL_EMALFDB;
213
+            }
218 214
 
219
-        /* normal trigger, get added */
220
-        ret = cli_pcre_addpatt(root, virname, trigger, pattern, cflags, offset, lsigid, options);
221
-        free(hexcpy);
222
-        return ret;
215
+            /* get NULL-ed */
216
+            *start = '\0';
217
+            *end = '\0';
218
+
219
+            /* get tokens-ed */
220
+            trigger = hexcpy;
221
+            pattern = start+1;
222
+            cflags = end+1;
223
+            if (*cflags == '\0') /* get compat-ed */
224
+                cflags = NULL;
225
+
226
+            /* normal trigger, get added */
227
+            ret = cli_pcre_addpatt(root, virname, trigger, pattern, cflags, offset, lsigid, options);
228
+            free(hexcpy);
229
+            return ret;
223 230
 #else
224
-        cli_errmsg("cli_parseadd(): cannot parse PCRE subsig without PCRE support\n");
225
-        return CL_EPARSE;
231
+            free(hexcpy);
232
+            cli_errmsg("cli_parseadd(): cannot parse PCRE subsig without PCRE support\n");
233
+            return CL_EPARSE;
226 234
 #endif
235
+        } else { /* get option-ed */
236
+            /* get NULL-ed */
237
+            *end = '\0';
238
+
239
+            /* get called */
240
+            ret = cli_parse_add(root, virname, hexcpy, end+1, rtype, type, offset, target, lsigid, options);
241
+            free(hexcpy);
242
+            return ret;
243
+        }
227 244
     }
228 245
     else if((wild = strchr(hexsig, '{'))) {
229 246
         if(sscanf(wild, "%c%u%c", &l, &range, &r) == 3 && l == '{' && r == '}' && range > 0 && range < 128) {
... ...
@@ -242,7 +250,7 @@ int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hex
242 242
             }
243 243
 
244 244
             strcat(hexcpy, ++wild);
245
-            ret = cli_parse_add(root, virname, hexcpy, rtype, type, offset, target, lsigid, options);
245
+            ret = cli_parse_add(root, virname, hexcpy, sigopts, rtype, type, offset, target, lsigid, options);
246 246
             free(hexcpy);
247 247
 
248 248
             return ret;
... ...
@@ -280,7 +288,7 @@ int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hex
280 280
                 *pt++ = 0;
281 281
             }
282 282
 
283
-            if((ret = cli_ac_addsig(root, virname, start, root->ac_partsigs, parts, i, rtype, type, mindist, maxdist, offset, lsigid, options))) {
283
+            if((ret = cli_ac_addsig(root, virname, start, sigopts, root->ac_partsigs, parts, i, rtype, type, mindist, maxdist, offset, lsigid, options))) {
284 284
                 cli_errmsg("cli_parse_add(): Problem adding signature (1).\n");
285 285
                 error = 1;
286 286
                 break;
... ...
@@ -363,7 +371,7 @@ int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hex
363 363
                 return CL_EMALFDB;
364 364
             }
365 365
 
366
-            if((ret = cli_ac_addsig(root, virname, pt, root->ac_partsigs, parts, i, rtype, type, 0, 0, offset, lsigid, options))) {
366
+            if((ret = cli_ac_addsig(root, virname, pt, sigopts, root->ac_partsigs, parts, i, rtype, type, 0, 0, offset, lsigid, options))) {
367 367
                 cli_errmsg("cli_parse_add(): Problem adding signature (2).\n");
368 368
                 free(pt);
369 369
                 return ret;
... ...
@@ -372,7 +380,7 @@ int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hex
372 372
             free(pt);
373 373
         }
374 374
     } else if(root->ac_only || type || lsigid || strpbrk(hexsig, "?([") || (root->bm_offmode && (!strcmp(offset, "*") || strchr(offset, ','))) || strstr(offset, "VI") || strchr(offset, '$')) {
375
-        if((ret = cli_ac_addsig(root, virname, hexsig, 0, 0, 0, rtype, type, 0, 0, offset, lsigid, options))) {
375
+        if((ret = cli_ac_addsig(root, virname, hexsig, sigopts, 0, 0, 0, rtype, type, 0, 0, offset, lsigid, options))) {
376 376
             cli_errmsg("cli_parse_add(): Problem adding signature (3).\n");
377 377
             return ret;
378 378
         }
... ...
@@ -656,7 +664,7 @@ static int cli_loaddb(FILE *fs, struct cl_engine *engine, unsigned int *signo, u
656 656
 
657 657
 	if(*pt == '=') continue;
658 658
 
659
-	if((ret = cli_parse_add(root, start, pt, 0, 0, "*", 0, NULL, options))) {
659
+	if((ret = cli_parse_add(root, start, pt, NULL, 0, 0, "*", 0, NULL, options))) {
660 660
 	    cli_dbgmsg("cli_loaddb: cli_parse_add failed on line %d\n", line);
661 661
 	    ret = CL_EMALFDB;
662 662
 	    break;
... ...
@@ -1056,7 +1064,7 @@ static int cli_loadndb(FILE *fs, struct cl_engine *engine, unsigned int *signo,
1056 1056
 	offset = tokens[2];
1057 1057
 	sig = tokens[3];
1058 1058
 
1059
-	if((ret = cli_parse_add(root, virname, sig, 0, 0, offset, target, NULL, options))) {
1059
+	if((ret = cli_parse_add(root, virname, sig, NULL, 0, 0, offset, target, NULL, options))) {
1060 1060
 	    ret = CL_EMALFDB;
1061 1061
 	    break;
1062 1062
 	}
... ...
@@ -1491,7 +1499,7 @@ static int load_oneldb(char *buffer, int chkpua, struct cl_engine *engine, unsig
1491 1491
             sig = tokens[3 + i];
1492 1492
         }
1493 1493
 
1494
-        if((ret = cli_parse_add(root, virname, sig, 0, 0, offset, target, lsigid, options)))
1494
+        if((ret = cli_parse_add(root, virname, sig, NULL, 0, 0, offset, target, lsigid, options)))
1495 1495
             return ret;
1496 1496
 
1497 1497
         if(sig[0] == '$' && i) {
... ...
@@ -1769,7 +1777,7 @@ static int cli_loadftm(FILE *fs, struct cl_engine *engine, unsigned int options,
1769 1769
 
1770 1770
         magictype = atoi(tokens[0]);
1771 1771
 	if(magictype == 1) { /* A-C */
1772
-	    if((ret = cli_parse_add(engine->root[0], tokens[3], tokens[2], rtype, type, tokens[1], 0, NULL, options)))
1772
+	    if((ret = cli_parse_add(engine->root[0], tokens[3], tokens[2], NULL, rtype, type, tokens[1], 0, NULL, options)))
1773 1773
 		break;
1774 1774
 
1775 1775
 	} else if ((magictype == 0) || (magictype == 4)) { /* memcmp() */
... ...
@@ -69,7 +69,7 @@
69 69
 
70 70
 char *cli_virname(const char *virname, unsigned int official);
71 71
 
72
-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);
72
+int cli_parse_add(struct cli_matcher *root, const char *virname, const char *hexsig, const char *sigopts, uint16_t rtype, uint16_t type, const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options);
73 73
 
74 74
 int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio);
75 75
 
... ...
@@ -21,6 +21,7 @@
21 21
 #ifndef __STR_H
22 22
 #define __STR_H
23 23
 
24
+#include <ctype.h>
24 25
 #include <sys/types.h>
25 26
 
26 27
 #include "cltypes.h"
... ...
@@ -31,6 +32,9 @@
31 31
 const char *cli_strcasestr(const char *haystack, const char *needle);
32 32
 #endif
33 33
 
34
+#include <stdio.h>
35
+#define cli_nocase(val) tolower(val)
36
+
34 37
 int cli_strbcasestr(const char *haystack, const char *needle);
35 38
 int cli_chomp(char *string);
36 39
 char *cli_strtok(const char *line, int field, const char *delim);
... ...
@@ -1998,7 +1998,7 @@ static void matchsig(const char *sig, const char *offset, int fd)
1998 1998
 	return;
1999 1999
     }
2000 2000
 
2001
-    if(cli_parse_add(engine->root[0], "test", sig, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
2001
+    if(cli_parse_add(engine->root[0], "test", sig, NULL, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
2002 2002
 	mprintf("!matchsig: Can't parse signature\n");
2003 2003
 	cl_engine_free(engine);
2004 2004
 	return;
... ...
@@ -2907,7 +2907,7 @@ static int dumpcerts(const struct optstruct *opts)
2907 2907
 	return -1;
2908 2908
     }
2909 2909
 
2910
-    if(cli_parse_add(engine->root[0], "test", "deadbeef", 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
2910
+    if(cli_parse_add(engine->root[0], "test", "deadbeef", NULL, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
2911 2911
 	mprintf("!dumpcerts: Can't parse signature\n");
2912 2912
 	cl_engine_free(engine);
2913 2913
 	return -1;
... ...
@@ -99,7 +99,7 @@ START_TEST (test_ac_scanbuff) {
99 99
 
100 100
 
101 101
     for(i = 0; ac_testdata[i].data; i++) {
102
-	ret = cli_parse_add(root, ac_testdata[i].virname, ac_testdata[i].hexsig, 0, 0, "*", 0, NULL, 0);
102
+	ret = cli_parse_add(root, ac_testdata[i].virname, ac_testdata[i].hexsig, NULL, 0, 0, "*", 0, NULL, 0);
103 103
 	fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
104 104
     }
105 105
 
... ...
@@ -138,11 +138,11 @@ START_TEST (test_bm_scanbuff) {
138 138
     ret = cli_bm_init(root);
139 139
     fail_unless(ret == CL_SUCCESS, "cli_bm_init() failed");
140 140
 
141
-    ret = cli_parse_add(root, "Sig1", "deadbabe", 0, 0, "*", 0, NULL, 0);
141
+    ret = cli_parse_add(root, "Sig1", "deadbabe", NULL, 0, 0, "*", 0, NULL, 0);
142 142
     fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
143
-    ret = cli_parse_add(root, "Sig2", "deadbeef", 0, 0, "*", 0, NULL, 0);
143
+    ret = cli_parse_add(root, "Sig2", "deadbeef", NULL, 0, 0, "*", 0, NULL, 0);
144 144
     fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
145
-    ret = cli_parse_add(root, "Sig3", "babedead", 0, 0, "*", 0, NULL, 0);
145
+    ret = cli_parse_add(root, "Sig3", "babedead", NULL, 0, 0, "*", 0, NULL, 0);
146 146
     fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
147 147
 
148 148
     ret = cli_bm_scanbuff((const unsigned char*)"blah\xde\xad\xbe\xef", 12, &virname, NULL, root, 0, NULL, NULL, NULL);
... ...
@@ -169,7 +169,7 @@ START_TEST (test_ac_scanbuff_allscan) {
169 169
 
170 170
 
171 171
     for(i = 0; ac_testdata[i].data; i++) {
172
-	ret = cli_parse_add(root, ac_testdata[i].virname, ac_testdata[i].hexsig, 0, 0, "*", 0, NULL, 0);
172
+	ret = cli_parse_add(root, ac_testdata[i].virname, ac_testdata[i].hexsig, NULL, 0, 0, "*", 0, NULL, 0);
173 173
 	fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
174 174
     }
175 175
 
... ...
@@ -214,11 +214,11 @@ START_TEST (test_bm_scanbuff_allscan) {
214 214
     ret = cli_bm_init(root);
215 215
     fail_unless(ret == CL_SUCCESS, "cli_bm_init() failed");
216 216
 
217
-    ret = cli_parse_add(root, "Sig1", "deadbabe", 0, 0, "*", 0, NULL, 0);
217
+    ret = cli_parse_add(root, "Sig1", "deadbabe", NULL, 0, 0, "*", 0, NULL, 0);
218 218
     fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
219
-    ret = cli_parse_add(root, "Sig2", "deadbeef", 0, 0, "*", 0, NULL, 0);
219
+    ret = cli_parse_add(root, "Sig2", "deadbeef", NULL, 0, 0, "*", 0, NULL, 0);
220 220
     fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
221
-    ret = cli_parse_add(root, "Sig3", "babedead", 0, 0, "*", 0, NULL, 0);
221
+    ret = cli_parse_add(root, "Sig3", "babedead", NULL, 0, 0, "*", 0, NULL, 0);
222 222
     fail_unless(ret == CL_SUCCESS, "cli_parse_add() failed");
223 223
 
224 224
     ret = cli_bm_scanbuff((const unsigned char*)"blah\xde\xad\xbe\xef", 12, &virname, NULL, root, 0, NULL, NULL, NULL);