1 /*
2 * Advanced Exchange Access (AXA) semantics for nmsg fields
3 *
4 * Copyright (c) 2014-2017 by Farsight Security, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include <axa/fields.h>
20 #include <config.h>
21
22 #include <nmsg/base/defs.h>
23 #include <nmsg/base/encode.pb-c.h>
24
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <string.h>
28
29 const axa_nmsg_field_t axa_null_field = {
30 .idx = AXA_NMSG_IDX_RSVD,
31 .class = {.idx = AXA_NMSG_IDX_NONE},
32 .rtype = {.idx = AXA_NMSG_IDX_NONE},
33 .owner = {.idx = AXA_NMSG_IDX_NONE},
34 .enm = {.idx = AXA_NMSG_IDX_NONE},
35 };
36
37
38 /**
39 * Vendor IDs and message types of messages that are worth decoding.
40 * Each (vendor,message type) pair has a list of fields that
41 * contain domains or IP addresses.
42 */
43 struct vm_entry {
44 struct vm_entry *next; /**< next vendor message */
45 axa_nmsg_idx_t vid; /**< nmsg vendor ID */
46 axa_nmsg_idx_t msgtype; /**< nmsg message type */
47 struct nmsg_msgmod *mod; /**< nmsg message module */
48 axa_nmsg_field_t *fields; /**< linked lisg of nmsg fields */
49 };
50
51 /** Vendor ID Message Type hash table */
52 typedef struct {
53 uint num_bins; /**< number of bins in this hash */
54 vm_entry_t *bins[]; /**< the hash table itself */
55 } vm_hash_t;
56 static vm_hash_t *vm_hash_tbl;
57
58 static inline vm_entry_t**
vm_hash_fnc(uint vid,uint msgtype)59 vm_hash_fnc(uint vid, uint msgtype)
60 {
61 uint n;
62
63 n = (vid << 12) | msgtype;
64 n %= vm_hash_tbl->num_bins;
65 return (&vm_hash_tbl->bins[n]);
66 }
67
68 static void
free_field(axa_nmsg_field_t * field)69 free_field(axa_nmsg_field_t *field)
70 {
71 axa_nmsg_sf_t *sf;
72
73 while ((sf = field->sf) != NULL) {
74 field->sf = sf->next;
75 free(sf);
76 }
77 free(field);
78 }
79
80 void
axa_unload_fields(void)81 axa_unload_fields(void)
82 {
83 struct vm_entry *vm;
84 axa_nmsg_field_t *field;
85 uint n;
86
87 if (vm_hash_tbl == NULL)
88 return;
89 for (n = 0; n < vm_hash_tbl->num_bins; ++n) {
90 while ((vm = vm_hash_tbl->bins[n]) != NULL) {
91 vm_hash_tbl->bins[n] = vm->next;
92 while ((field = vm->fields) != NULL) {
93 vm->fields = field->next;
94 free_field(field);
95 }
96 free(vm);
97 }
98 }
99 free(vm_hash_tbl);
100 vm_hash_tbl = NULL;
101 }
102
103 /* Do we know a vendor ID and message type? */
104 const axa_nmsg_field_t *
axa_msg_fields(const nmsg_message_t msg)105 axa_msg_fields(const nmsg_message_t msg)
106 {
107 axa_nmsg_idx_t vid, msgtype;
108 const vm_entry_t *e;
109
110 if (vm_hash_tbl == NULL)
111 return (NULL);
112
113 vid = nmsg_message_get_vid(msg);
114 msgtype = nmsg_message_get_msgtype(msg);
115 for (e = *vm_hash_fnc(vid, msgtype); e != NULL; e = e->next) {
116 if (e->vid == vid && e->msgtype == msgtype)
117 return (e->fields);
118 }
119 return (NULL);
120 }
121
122 static nmsg_message_t
message_init(struct nmsg_msgmod * mod,const char * fname,uint line_num,const char * fields_file)123 message_init(struct nmsg_msgmod *mod, const char *fname,
124 uint line_num, const char *fields_file)
125 {
126 nmsg_message_t msg;
127
128 msg = nmsg_message_init(mod);
129 if (msg == NULL)
130 axa_error_msg("nmsg_message_init() failed for \"%s\""
131 " in line %d of \"%s\"",
132 fname, line_num, fields_file);
133 return (msg);
134 }
135
136 static bool
get_enum_value(struct nmsg_msgmod * mod,const char * fname,const char * enum_name,uint * valp,uint line_num,const char * fields_file)137 get_enum_value(struct nmsg_msgmod *mod, const char *fname,
138 const char *enum_name, uint *valp,
139 uint line_num, const char *fields_file)
140 {
141 nmsg_message_t msg;
142 nmsg_res res;
143
144 msg = message_init(mod, fname, line_num, fields_file);
145 if (msg == NULL) {
146 axa_error_msg("unrecognized %s enum field name \"%s\""
147 " in line %d of \"%s\"",
148 fname, enum_name, line_num, fields_file);
149 return (false);
150 }
151
152 res = nmsg_message_enum_name_to_value(msg, fname, enum_name, valp);
153 nmsg_message_destroy(&msg);
154
155 if (res != nmsg_res_success) {
156 axa_error_msg("unrecognized %s enum value \"%s\""
157 " in line %d of \"%s\"",
158 fname, enum_name, line_num, fields_file);
159 return (false);
160 }
161 return (true);
162 }
163
164 /* Get the nmsg index of a field by its name for a line in the fields file. */
165 static axa_nmsg_idx_t
get_field_idx(struct nmsg_msgmod * mod,const char * ftype,const char * fname,uint line_num,const char * fields_file)166 get_field_idx(struct nmsg_msgmod *mod, /* This module */
167 const char *ftype, /* our name for the field type */
168 const char *fname, /* target field name */
169 uint line_num, const char *fields_file)
170 {
171 uint idx;
172 nmsg_message_t msg;
173 nmsg_res res;
174
175 msg = message_init(mod, fname, line_num, fields_file);
176 if (msg == NULL)
177 return (AXA_NMSG_IDX_ERROR);
178
179 res = nmsg_message_get_field_idx(msg, fname, &idx);
180 nmsg_message_destroy(&msg);
181
182 if (res != nmsg_res_success) {
183 axa_error_msg("unrecognized %s%sfield name \"%s\""
184 " in line %d of \"%s\"",
185 ftype, ftype[0] != '\0' ? " " : "",
186 fname, line_num, fields_file);
187 return (AXA_NMSG_IDX_ERROR);
188 }
189
190 if (idx >= AXA_NMSG_IDX_RSVD) {
191 axa_error_msg("%s%sfield name \"%s\"=%d and > AXA limit %d"
192 " in line %d of \"%s\"",
193 ftype, ftype[0] != '\0' ? " " : "",
194 fname, idx, AXA_NMSG_IDX_RSVD,
195 line_num, fields_file);
196 return (AXA_NMSG_IDX_ERROR);
197 }
198
199 return (idx);
200 }
201
202 /* Get the nmsg index of a helper field and whether to use val_idx=0 */
203 static bool
get_help_idx(axa_nmsg_help_t * help,struct nmsg_msgmod * mod,const char * ftype,const char * fname,uint line_num,const char * fields_file)204 get_help_idx(axa_nmsg_help_t *help, /* results here */
205 struct nmsg_msgmod *mod, /* this module */
206 const char *ftype, /* our name for the field type */
207 const char *fname, /* helper nmsg field name */
208 uint line_num, const char *fields_file)
209 {
210 help->idx = get_field_idx(mod, ftype, fname, line_num, fields_file);
211 return (help->idx < AXA_NMSG_IDX_RSVD);
212 }
213
214 /* Parse the content field of a fields file line to get a content type. */
215 static axa_fc_t
get_fc(const char * content,uint line_num,const char * fields_file)216 get_fc(const char *content, uint line_num, const char *fields_file)
217 {
218 typedef struct {
219 const char *s;
220 axa_fc_t fc;
221 } fc_tbl_t;
222 static fc_tbl_t fc_tbl[] = {
223 {"IP-dgram", AXA_FC_IP_DGRAM},
224 {"IP", AXA_FC_IP},
225 {"IP-ASCII", AXA_FC_IP_ASCII},
226 {"domain", AXA_FC_DOM},
227 {"domain-ASCII", AXA_FC_DOM_ASCII},
228 {"host", AXA_FC_HOST},
229 {"rdata", AXA_FC_RDATA},
230 {"dns", AXA_FC_DNS},
231 {"json", AXA_FC_JSON},
232 };
233 const fc_tbl_t *tp;
234
235 /* This is done only at start-up, so do not worry about speed. */
236 for (tp = fc_tbl; tp <= AXA_LAST(fc_tbl); ++tp) {
237 if (strcasecmp(content, tp->s) == 0)
238 return (tp->fc);
239 }
240
241 if (content[0] == '\0') {
242 axa_error_msg("missing field content"
243 " in line %d of \"%s\"",
244 line_num, fields_file);
245 } else {
246 axa_error_msg("unrecognized field content \"%s\""
247 " in line %d of \"%s\"",
248 content, line_num, fields_file);
249 }
250 return (AXA_FC_UNKNOWN);
251 }
252
253 static char *
get_subsubval(char * subval)254 get_subsubval(char *subval)
255 {
256 char *p;
257
258 p = strchr(subval, '=');
259 if (p == NULL || p[1] == '\0')
260 return (NULL);
261 *p++ = '\0';
262 return (p);
263 }
264
265 /*
266 * Read the fields file to build the tables of known vendor IDs,
267 * message types, and fields.
268 */
269 void
axa_load_fields(const char * fields_file0)270 axa_load_fields(const char *fields_file0)
271 {
272 char *fields_file;
273 FILE *f;
274 char *line_buf;
275 size_t line_buf_size;
276 uint line_num;
277 const char *line;
278 char *p;
279 struct nmsg_msgmod *mod;
280 vm_entry_t *vm_list, *vm, **vmp;
281 axa_nmsg_field_t *field;
282 axa_nmsg_sf_t *sf, *sf2;
283 char fc[AXA_FIELD_NM_LEN];
284 char subtype[AXA_FIELD_NM_LEN];
285 char subval[AXA_FIELD_NM_LEN];
286 uint vid, msgtype;
287 uint num_vm, num_vm_bins;
288 size_t len;
289
290 axa_unload_fields();
291
292 /*
293 * Use a specified file, or default to $AXACONF/fields,
294 * or $HOME/.axa/fields, or AXACONFDIR/fields.
295 */
296 if (fields_file0 != NULL && *fields_file0 != '\0') {
297 fields_file = axa_strdup(fields_file0);
298 f = fopen(fields_file, "r");
299 } else {
300 f = NULL;
301 p = getenv("AXACONF");
302 if (p == NULL) {
303 fields_file = NULL;
304 } else {
305 axa_asprintf(&fields_file, "%s/%s",
306 p, "fields");
307 f = fopen(fields_file, "r");
308 }
309 if (f == NULL) {
310 if (fields_file != NULL)
311 free(fields_file);
312 p = getenv("HOME");
313 if (p == NULL) {
314 fields_file = NULL;
315 } else {
316 axa_asprintf(&fields_file, "%s/%s", p, "fields");
317 f = fopen(fields_file, "r");
318 }
319 }
320 if (f == NULL) {
321 if (fields_file != NULL)
322 free(fields_file);
323 fields_file = strdup(AXACONFDIR"/fields");
324 f = fopen(fields_file, "r");
325 }
326 }
327 if (f == NULL) {
328 axa_error_msg("cannot open \"%s\": %s",
329 fields_file, strerror(errno));
330 free(fields_file);
331 return;
332 }
333
334 line_buf = NULL;
335 line_buf_size = 0;
336
337 vm_list = NULL;
338 num_vm = 0;
339 line_num = 0;
340 field = NULL;
341 for (;;) {
342 next_line:
343 if (field != NULL) {
344 free_field(field);
345 field = NULL;
346 }
347
348 line = axa_fgetln(f, fields_file, &line_num,
349 &line_buf, &line_buf_size);
350 if (line == NULL)
351 break;
352
353 field = AXA_SALLOC(axa_nmsg_field_t);
354 *field = axa_null_field;
355 field->line_num = line_num;
356
357 /* get the vendor name and message type from the line */
358 if (0 > axa_get_token(field->vname, sizeof(field->vname),
359 &line, AXA_WHITESPACE)
360 || (vid = nmsg_msgmod_vname_to_vid(field->vname)) == 0) {
361 axa_error_msg("unrecognized vendor \"%s\""
362 " in line %d of \"%s\"",
363 field->vname, line_num, fields_file);
364 continue;
365 }
366 if (vid >= AXA_NMSG_IDX_RSVD) {
367 axa_error_msg("vendor \"%s\" >= AXA limit %d"
368 " in line %d of \"%s\"",
369 field->vname, AXA_NMSG_IDX_RSVD,
370 line_num, fields_file);
371 continue;
372 }
373
374 if (*line == '\0') {
375 axa_error_msg("missing message type and field name"
376 " in line %d of \"%s\"",
377 line_num, fields_file);
378 continue;
379 }
380 if (0 > axa_get_token(field->mname, sizeof(field->mname),
381 &line, AXA_WHITESPACE)
382 || (msgtype = nmsg_msgmod_mname_to_msgtype(vid,
383 field->mname)) == 0) {
384 axa_error_msg("unrecognized message type \"%s\""
385 " in line %d of \"%s\"",
386 field->mname, line_num, fields_file);
387 continue;
388 }
389 if (msgtype >= AXA_NMSG_IDX_RSVD) {
390 axa_error_msg("message type \"%s\" >= AXA limit %d"
391 " in line %d of \"%s\"",
392 field->mname,
393 AXA_NMSG_IDX_RSVD, line_num, fields_file);
394 continue;
395 }
396
397 /* Search the list of known (vendor,message type) pairs.
398 * If this pair is new, get their libnmsg ordinals. */
399 for (vm = vm_list; vm != NULL; vm = vm->next) {
400 if (vm->vid == vid && vm->msgtype == msgtype)
401 break;
402 }
403 if (vm != NULL) {
404 mod = vm->mod;
405 } else {
406 mod = nmsg_msgmod_lookup_byname(field->vname,
407 field->mname);
408 if (mod == NULL) {
409 axa_error_msg("cannot find module for vendor ID"
410 " \"%s\" and message type \"%s\""
411 " in line %d of \"%s\"",
412 field->vname, field->mname,
413 line_num, fields_file);
414 continue;
415 }
416 }
417
418 /* Add the field specified by the rest of the line to the
419 * list of interesting fields of this (vendor, message type)
420 * pair. Start by parsing the name of the field. */
421 if (*line == '\0') {
422 axa_error_msg("missing field name"
423 " in line %d of \"%s\"",
424 line_num, fields_file);
425 continue;
426 }
427 if (0 > axa_get_token(field->name, sizeof(field->name),
428 &line, AXA_WHITESPACE)) {
429 axa_error_msg("bad field name"
430 " in line %d of \"%s\"",
431 line_num, fields_file);
432 continue;
433 }
434 field->idx = get_field_idx(mod, "", field->name,
435 line_num, fields_file);
436 if (field->idx >= AXA_NMSG_IDX_RSVD)
437 continue;
438
439 /* parse the content type */
440 axa_get_token(fc, sizeof(fc), &line, AXA_WHITESPACE);
441 field->fc = get_fc(fc, line_num, fields_file);
442 if (field->fc == AXA_FC_UNKNOWN)
443 continue;
444
445 while (*line != '\0') {
446 /* get next optional "subtype=fname..."
447 * subtype is {rtype|class|...}
448 * subval is the name of the nmsg field with the
449 * required subtype data. */
450 if (0 > axa_get_token(subtype, sizeof(subtype),
451 &line, "=")
452 || strpbrk(subtype, AXA_WHITESPACE) != NULL) {
453 axa_error_msg("unrecognized \"%s\""
454 " in line %d of \"%s\"",
455 subtype, line_num, fields_file);
456 goto next_line;
457 }
458 if (0 > axa_get_token(subval, sizeof(subval),
459 &line, AXA_WHITESPACE)
460 || subval[0] == '\0') {
461 axa_error_msg("unrecognized \"%s=%s\""
462 " in line %d of \"%s\"",
463 subtype, subval,
464 line_num, fields_file);
465 goto next_line;
466 }
467
468 if ((field->fc == AXA_FC_RDATA
469 || field->fc == AXA_FC_DOM)
470 && field->class.idx == AXA_NMSG_IDX_NONE
471 && strcasecmp(subtype, "class") == 0) {
472 if (!get_help_idx(&field->class,
473 mod, subtype, subval,
474 line_num, fields_file))
475 goto next_line;
476
477 } else if (field->fc == AXA_FC_RDATA
478 && field->rtype.idx == AXA_NMSG_IDX_NONE
479 && strcasecmp(subtype, "rtype") == 0) {
480 if (!get_help_idx(&field->rtype,
481 mod, subtype, subval,
482 line_num, fields_file))
483 goto next_line;
484
485 } else if (field->fc == AXA_FC_RDATA
486 && field->owner.idx == AXA_NMSG_IDX_NONE
487 && strcasecmp(subtype, "oname") == 0) {
488 if (!get_help_idx(&field->owner,
489 mod, subtype, subval,
490 line_num, fields_file))
491 goto next_line;
492
493 } else if (field->enm.idx == AXA_NMSG_IDX_NONE
494 && strcasecmp(subtype, "enum") == 0
495 && (p = get_subsubval(subval)) != '\0') {
496 field->enm.idx = get_field_idx(mod,
497 subtype, subval,
498 line_num, fields_file);
499 if (field->enm.idx >= AXA_NMSG_IDX_RSVD)
500 goto next_line;
501 if (!get_enum_value(mod, subval,
502 p, &field->enm_val,
503 line_num, fields_file))
504 goto next_line;
505
506 } else if (field->fc == AXA_FC_JSON
507 && strcasecmp(subtype, "sfield") == 0
508 && (p = get_subsubval(subval)) != '\0') {
509 sf = axa_zalloc(sizeof(axa_nmsg_sf_t)
510 +strlen(subval)+1);
511 sf->next = field->sf;
512 field->sf = sf;
513 sf->len = strlen(subval);
514 memcpy(sf->name, subval, sf->len);
515 sf->fc = get_fc(p, line_num, fields_file);
516 switch (sf->fc) {
517 case AXA_FC_IP_ASCII:
518 case AXA_FC_DOM_ASCII:
519 case AXA_FC_HOST:
520 break;
521 case AXA_FC_UNKNOWN:
522 case AXA_FC_IP_DGRAM:
523 case AXA_FC_IP:
524 case AXA_FC_DOM:
525 case AXA_FC_RDATA:
526 case AXA_FC_DNS:
527 case AXA_FC_JSON:
528 default:
529 goto next_line;
530 }
531 for (sf2 = sf->next;
532 sf2 != NULL;
533 sf2 = sf2->next) {
534 if (strcmp(sf->name, sf2->name) == 0) {
535 axa_error_msg("duplicate sfield=%s"
536 " in line %d of \"%s\"",
537 sf->name,
538 line_num, fields_file);
539 goto next_line;
540 }
541 }
542 } else {
543 axa_error_msg("unrecognized \"%s=%s\""
544 " in line %d of \"%s\"",
545 subtype, subval,
546 line_num, fields_file);
547 goto next_line;
548 }
549 }
550
551 if (field->class.idx == AXA_NMSG_IDX_NONE
552 && (field->fc == AXA_FC_DOM
553 || field->fc == AXA_FC_RDATA)) {
554 axa_error_msg("missing \"class=field\""
555 " in line %d of \"%s\"",
556 line_num, fields_file);
557 continue;
558 }
559 if (field->rtype.idx == AXA_NMSG_IDX_NONE
560 && field->fc == AXA_FC_RDATA) {
561 axa_error_msg("missing \"rtype=field\""
562 " in line %d of \"%s\"",
563 line_num, fields_file);
564 continue;
565 }
566
567 /* If we have previously seen this vendor and message type,
568 * ensure that we have not see this field. */
569 if (vm != NULL) {
570 const axa_nmsg_field_t *field2;
571
572 for (field2 = vm->fields;
573 field2 != NULL;
574 field2 = field2->next) {
575 if (field2->idx == field->idx
576 && field->enm.idx == field2->enm.idx
577 && field->enm_val == field2->enm_val)
578 break;
579 }
580 if (field2 != NULL) {
581 axa_error_msg("duplicate vendor ID,"
582 " message type, and field name"
583 " in lines %d and %d of \"%s\"",
584 line_num, field2->line_num,
585 fields_file);
586 continue;
587 }
588 } else {
589 vm = AXA_SALLOC(vm_entry_t);
590 vm->vid = vid;
591 vm->msgtype = msgtype;
592 vm->mod = mod;
593 vm->next = vm_list;
594 vm_list = vm;
595 ++num_vm;
596 }
597 field->next = vm->fields;
598 field->vm = vm;
599 vm->fields = field;
600 field = NULL;
601 }
602 if (field != NULL) {
603 free_field(field);
604 field = NULL;
605 }
606 fclose(f);
607 if (line_buf != NULL)
608 free(line_buf);
609
610 if (num_vm == 0) {
611 axa_error_msg("no fields defined in \"%s\"", fields_file);
612 free(fields_file);
613 return;
614 }
615
616 /* Move the list of lists of fields into a hash table.
617 * Be generous because this hash table is small. */
618 num_vm_bins = axa_hash_divisor(num_vm*2+20, false);
619 len = sizeof(*vm_hash_tbl) + sizeof(vm_hash_tbl->bins[0])*num_vm_bins;
620 vm_hash_tbl = axa_zalloc(len);
621
622 vm_hash_tbl->num_bins = num_vm_bins;
623 while ((vm = vm_list) != NULL) {
624 vm_list = vm->next;
625 vmp = vm_hash_fnc(vm->vid, vm->msgtype);
626 vm->next = *vmp;
627 *vmp = vm;
628 }
629
630 free(fields_file);
631 }
632
633 /* Get the contents of a "helper" field for a fields file line */
634 bool
axa_get_helper(axa_emsg_t * emsg,const nmsg_message_t msg,const axa_nmsg_help_t * help,axa_nmsg_idx_t val_idx,void * val,size_t * val_len,size_t min_val_len,size_t max_val_len,axa_helper_cache_t * cache)635 axa_get_helper(axa_emsg_t *emsg, const nmsg_message_t msg,
636 const axa_nmsg_help_t *help, axa_nmsg_idx_t val_idx,
637 void *val, size_t *val_len,
638 size_t min_val_len, size_t max_val_len,
639 axa_helper_cache_t *cache)
640 {
641 void *data;
642 size_t data_len;
643 uint cn;
644 nmsg_res res;
645
646 if (help->idx >= AXA_NMSG_IDX_RSVD) {
647 axa_pemsg(emsg, "invalid field index %#x", help->idx);
648 return (false);
649 }
650
651 /* Be fast about repeated fetches of the helper values */
652 if (cache != NULL) {
653 for (cn = 0; cn < cache->len; ++cn) {
654 if (cache->e[cn].idx == help->idx
655 && cache->e[cn].val_idx == val_idx) {
656 if (min_val_len == sizeof(cache->e[cn].val)
657 && max_val_len == sizeof(cache->e[cn].val)) {
658 memcpy(val, &cache->e[cn].val,
659 min_val_len);
660 if (val_len != NULL)
661 *val_len = sizeof(cache->e[cn].val);
662 return (true);
663 }
664 break;
665 }
666 }
667 }
668
669 res = nmsg_message_get_field_by_idx(msg, help->idx, val_idx,
670 &data, &data_len);
671 if (res != nmsg_res_success) {
672 axa_pemsg(emsg, "nmsg_message_get_field_by_idx(%s): %s",
673 axa_get_field_name(msg, help->idx),
674 nmsg_res_lookup(res));
675 return (false);
676 }
677 if (data_len < min_val_len || data_len > max_val_len) {
678 axa_pemsg(emsg, "%s size=%zd not >=%zd and <=%zd",
679 axa_get_field_name(msg, help->idx), data_len,
680 min_val_len, max_val_len);
681 return (false);
682 }
683
684 memcpy(val, data, data_len);
685 if (val_len != NULL)
686 *val_len = data_len;
687
688 if (cache != NULL && (cn = cache->len) < AXA_HELPER_CACHE_LEN
689 && min_val_len == data_len
690 && min_val_len == sizeof(cache->e[cn].val)
691 && max_val_len == sizeof(cache->e[cn].val)) {
692 cache->e[cn].idx = help->idx;
693 cache->e[cn].val_idx = val_idx;
694 memcpy(&cache->e[cn].val, data, sizeof(cache->e[cn].val));
695 ++cache->len;
696 }
697
698 return (true);
699 }
700
701