1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 1998-2013 Sourcefire, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License Version 2 as published
7 // by the Free Software Foundation. You may not use, modify or distribute
8 // this program under any other version of the GNU General Public License.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 //--------------------------------------------------------------------------
19
20 /*
21 * Adam Keeton
22 * sf_ipvar.c
23 * 11/17/06
24 *
25 * Library for IP variables.
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include "sf_ipvar.h"
33
34 #include <cassert>
35 #include "utils/util.h"
36
37 #include "sf_cidr.h"
38 #include "sf_vartable.h"
39
40 #ifdef UNIT_TEST
41 #include "catch/snort_catch.h"
42 #include "utils/util_cstring.h"
43 #endif
44
45 using namespace snort;
46
47 #define LIST_OPEN '['
48 #define LIST_CLOSE ']'
49
50 static SfIpRet sfvar_list_compare(sfip_node_t*, sfip_node_t*);
51 static inline void sfip_node_free(sfip_node_t*);
52 static inline void sfip_node_freelist(sfip_node_t*);
53
_alloc_var()54 static inline sfip_var_t* _alloc_var()
55 {
56 return (sfip_var_t*)snort_calloc(sizeof(sfip_var_t));
57 }
58
sfvar_free(sfip_var_t * var)59 void sfvar_free(sfip_var_t* var)
60 {
61 if (!var)
62 return;
63
64 if (var->name)
65 snort_free(var->name);
66
67 if (var->value)
68 snort_free(var->value);
69
70 if (var->mode == SFIP_LIST)
71 {
72 sfip_node_freelist(var->head);
73 sfip_node_freelist(var->neg_head);
74 }
75 else if (var->mode == SFIP_TABLE)
76 {
77 // FIXIT-L SFIP_TABLE free unimplemented
78 }
79
80 snort_free(var);
81 }
82
83 /* Allocates and returns an IP node described by 'str' */
sfipnode_alloc(const char * str,SfIpRet * status)84 static sfip_node_t* sfipnode_alloc(const char* str, SfIpRet* status)
85 {
86 // FIXIT-L rename variables from ret to something with more descriptive
87 // this code smell that afflicts 55 source files and all should be fixed
88 sfip_node_t* ret;
89 SfIpRet rc;
90
91 if (!str)
92 {
93 if (status)
94 *status = SFIP_ARG_ERR;
95 return nullptr;
96 }
97
98 ret = (sfip_node_t*)snort_calloc(sizeof(sfip_node_t));
99
100 /* Check if this string starts with a '!', if so,
101 * then the node needs to be negated */
102 if (*str == '!')
103 {
104 str++;
105 ret->flags |= SFIP_NEGATED;
106 }
107
108 while ( isspace(*str) )
109 ++str;
110
111 /* Check if this is an "any" */
112 if (!strncasecmp(str, "any", 3))
113 {
114 /* Make sure they're not doing !any, which is meaningless */
115 if (ret->flags & SFIP_NEGATED)
116 {
117 if (status)
118 *status = SFIP_ARG_ERR;
119 snort_free(ret);
120 return nullptr;
121 }
122
123 ret->flags |= SFIP_ANY;
124
125 ret->ip = new SfCidr();
126 if ((rc = ret->ip->set("0.0.0.0")) != SFIP_SUCCESS)
127 {
128 if (status)
129 *status = rc;
130 sfip_node_free(ret);
131 return nullptr;
132 }
133
134 if (status)
135 *status = SFIP_SUCCESS;
136 }
137 else
138 {
139 ret->ip = new SfCidr();
140 if ((rc = ret->ip->set(str)) != SFIP_SUCCESS)
141 {
142 if (status)
143 *status = rc;
144 sfip_node_free(ret);
145 return nullptr;
146 }
147 }
148
149 /* Check if this is a negated, zeroed IP (equivalent of a "!any") */
150 if (!ret->ip->is_set() && (ret->flags & SFIP_NEGATED))
151 {
152 if (status)
153 *status = SFIP_NOT_ANY;
154 sfip_node_free(ret);
155 return nullptr;
156 }
157
158 return ret;
159 }
160
sfip_node_free(sfip_node_t * node)161 static inline void sfip_node_free(sfip_node_t* node)
162 {
163 if ( !node )
164 return;
165
166 if ( node->ip )
167 delete node->ip;
168
169 snort_free(node);
170 }
171
sfip_node_freelist(sfip_node_t * root)172 static inline void sfip_node_freelist(sfip_node_t* root)
173 {
174 sfip_node_t* node;
175
176 if ( !root )
177 return;
178
179 for ( node = root; node; node = root )
180 {
181 root = root->next;
182 sfip_node_free(node);
183 }
184 }
185
_sfvar_deep_copy_list(const sfip_node_t * idx)186 static inline sfip_node_t* _sfvar_deep_copy_list(const sfip_node_t* idx)
187 {
188 sfip_node_t* ret = nullptr;
189 sfip_node_t* temp = nullptr;
190
191 for (; idx; idx = idx->next)
192 {
193 sfip_node_t* prev = temp;
194
195 temp = (sfip_node_t*)snort_calloc(sizeof(*temp));
196 temp->ip = new SfCidr();
197
198 temp->flags = idx->flags;
199 temp->addr_flags = idx->addr_flags;
200
201 /* If it's an "any", there may be no IP object */
202 if (idx->ip)
203 memcpy(temp->ip, idx->ip, sizeof(*temp->ip));
204
205 if (prev)
206 prev->next = temp;
207 else
208 ret = temp;
209 }
210 return ret;
211 }
212
213 /* Returns true if the node is contained by a network from the sorted list; deletes/consumes
214 * the node. Returns false otherwise; does not delete the node but removes any network from the
215 * list that is contained by the node so that the caller can insert the node without overlap. */
list_contains_node(sfip_node_t * & head,sfip_node_t * & tail,uint32_t & count,sfip_node_t * & node)216 static inline bool list_contains_node(sfip_node_t*& head, sfip_node_t*& tail, uint32_t& count,
217 sfip_node_t*& node)
218 {
219 sfip_node_t* cur = nullptr;
220 sfip_node_t* prev = nullptr;
221
222 if ( node->ip->get_family() == AF_INET )
223 {
224 for ( cur = head; cur; prev = cur, cur = cur->next )
225 if ( !cur->ip->is_set() or cur->ip->get_family() != AF_INET )
226 continue;
227 else if ( node->ip->fast_cont4(*cur->ip->get_addr()) )
228 break;
229 else if ( cur->ip->fast_cont4(*node->ip->get_addr()) )
230 {
231 sfip_node_free(node);
232 return true;
233 }
234 }
235 else if ( node->ip->get_family() == AF_INET6 )
236 {
237 for ( cur = head; cur; prev = cur, cur = cur->next )
238 if ( !cur->ip->is_set() or cur->ip->get_family() != AF_INET6 )
239 continue;
240 else if ( node->ip->fast_cont6(*cur->ip->get_addr()) )
241 break;
242 else if ( cur->ip->fast_cont6(*node->ip->get_addr()) )
243 {
244 sfip_node_free(node);
245 return true;
246 }
247 }
248
249 if ( cur ) // this network is contained by the node
250 {
251 if ( prev )
252 {
253 prev->next = cur->next;
254 if ( !prev->next )
255 tail = prev;
256 }
257 else if ( cur->next )
258 head = cur->next;
259 else // only one node
260 {
261 head = tail = node;
262 sfip_node_free(cur);
263 return true;
264 }
265 sfip_node_free(cur); // overlaps removed so that caller can insert the node
266 --count;
267 }
268 return false;
269 }
270
271 /* Deep copy. Returns identical, new, linked list of sfipnodes. */
sfvar_deep_copy(const sfip_var_t * var)272 sfip_var_t* sfvar_deep_copy(const sfip_var_t* var)
273 {
274 sfip_var_t* ret;
275
276 if (!var)
277 return nullptr;
278
279 ret = (sfip_var_t*)snort_calloc(sizeof(*ret));
280
281 ret->mode = var->mode;
282 ret->head = _sfvar_deep_copy_list(var->head);
283 ret->neg_head = _sfvar_deep_copy_list(var->neg_head);
284 ret->head_count = var->head_count;
285 ret->neg_head_count = var->neg_head_count;
286
287 return ret;
288 }
289
merge_lists(sfip_node_t * list1,sfip_node_t * list2,uint16_t list1_len,uint16_t list2_len,uint32_t & merge_len)290 static sfip_node_t* merge_lists(sfip_node_t* list1, sfip_node_t* list2, uint16_t list1_len,
291 uint16_t list2_len, uint32_t& merge_len)
292 {
293 sfip_node_t* listHead = nullptr, * merge_list = nullptr, * tmp = nullptr, * node = nullptr;
294 uint32_t num_nodes = 0;
295
296 if (!list1 && !list2)
297 {
298 merge_len = 0;
299 return nullptr;
300 }
301 if (!list1)
302 {
303 merge_len = list2_len;
304 return list2;
305 }
306 if (!list2)
307 {
308 merge_len = list1_len;
309 return list1;
310 }
311
312 /*Both lists are sorted and not null. If list1 or list2 contains "any", free the other list*/
313 if (list1->flags & SFIP_ANY)
314 {
315 merge_len = list1_len;
316 sfip_node_freelist(list2);
317 return list1;
318 }
319
320 if (list2->flags & SFIP_ANY)
321 {
322 merge_len = list2_len;
323 sfip_node_freelist(list1);
324 return list2;
325 }
326
327 /*Iterate till one of the list is null. Append each node to merge_list*/
328 while (list1 && list2)
329 {
330 if ( num_nodes )
331 {
332 tmp = list1->next;
333 if ( list_contains_node(listHead, merge_list, num_nodes, list1) )
334 {
335 list1 = tmp;
336 list1_len--;
337 continue;
338 }
339
340 tmp = list2->next;
341 if ( list_contains_node(listHead, merge_list, num_nodes, list2) )
342 {
343 list2 = tmp;
344 list2_len--;
345 continue;
346 }
347 }
348
349 SfIpRet ret = list1->ip->compare(*(list2->ip));
350 if (ret == SFIP_LESSER)
351 {
352 node = list1;
353 list1 = list1->next;
354 list1_len--;
355 }
356 else if (ret == SFIP_GREATER)
357 {
358 node = list2;
359 list2 = list2->next;
360 list2_len--;
361 }
362 else if (ret == SFIP_EQUAL)
363 {
364 node = list1;
365 list1 = list1->next;
366 /*Free the duplicate node*/
367 tmp = list2->next;
368 sfip_node_free(list2);
369 list2 = tmp;
370
371 list1_len--;
372 list2_len--;
373 }
374 else // SFIP_FAILURE
375 break;
376
377 if (!merge_list)
378 {
379 merge_list = node;
380 listHead = node;
381 }
382 else
383 {
384 merge_list->next = node;
385 merge_list = merge_list->next;
386 }
387 node->next = nullptr;
388 num_nodes++;
389 }
390
391 /*list2 is null. Append list1*/
392 while ( list1 )
393 {
394 tmp = list1->next;
395 if ( !list_contains_node(listHead, merge_list, num_nodes, list1) and merge_list )
396 {
397 merge_list->next = list1;
398 num_nodes += list1_len;
399 break;
400 }
401 list1 = tmp;
402 }
403
404 /*list1 is null. Append list2*/
405 while ( list2 )
406 {
407 tmp = list2->next;
408 if ( !list_contains_node(listHead, merge_list, num_nodes, list2) and merge_list )
409 {
410 merge_list->next = list2;
411 num_nodes += list2_len;
412 break;
413 }
414 list2 = tmp;
415 }
416
417 merge_len = num_nodes;
418 return listHead;
419 }
420
sfvar_add(sfip_var_t * dst,sfip_var_t * src)421 SfIpRet sfvar_add(sfip_var_t* dst, sfip_var_t* src)
422 {
423 sfip_var_t* copiedvar;
424
425 assert(dst and src);
426
427 if ((copiedvar = sfvar_deep_copy(src)) == nullptr)
428 {
429 return SFIP_ALLOC_ERR;
430 }
431
432 dst->head = merge_lists(dst->head, copiedvar->head, dst->head_count,
433 copiedvar->head_count, dst->head_count);
434 dst->neg_head = merge_lists(dst->neg_head, copiedvar->neg_head, dst->neg_head_count,
435 copiedvar->neg_head_count, dst->neg_head_count);
436
437 snort_free(copiedvar);
438
439 return SFIP_SUCCESS;
440 }
441
442 // Adds the nodes in 'src' to the variable 'dst'
443 // The mismatch of types is for ease-of-supporting Snort4 and
444 // Snort6 simultaneously
sfvar_add_node(sfip_var_t * var,sfip_node_t * node,int negated)445 static SfIpRet sfvar_add_node(sfip_var_t* var, sfip_node_t* node, int negated)
446 {
447 sfip_node_t* p;
448 sfip_node_t* swp;
449 sfip_node_t** head;
450 uint32_t* count;
451
452 if (!var || !node)
453 return SFIP_ARG_ERR;
454
455 // As of this writing, 11/20/06, nodes are always added to
456 // the list, regardless of the mode (list or table).
457
458 if (negated)
459 {
460 head = &var->neg_head;
461 count = &var->neg_head_count;
462 }
463 else
464 {
465 head = &var->head;
466 count = &var->head_count;
467 }
468
469 if (!(*head))
470 {
471 *head = node;
472 ++*count;
473 return SFIP_SUCCESS;
474 }
475
476 /*If head node is any, do not add anything else*/
477 if ((*head)->flags & SFIP_ANY)
478 {
479 sfip_node_free(node);
480 return SFIP_SUCCESS;
481 }
482
483 /* "Anys" should always be inserted first */
484 if (node->flags & SFIP_ANY)
485 {
486 /*Free the list when adding any*/
487 while (*head)
488 {
489 swp = (*head)->next;
490 sfip_node_free(*head);
491 *head = swp;
492 }
493 *head = node;
494 *count = 1;
495 return SFIP_SUCCESS;
496 }
497
498 if ( list_contains_node(*head, swp, *count, node) )
499 return SFIP_SUCCESS;
500
501 /* To insert the new node, first check if this IP is less than the head's IP */
502 SfIpRet node_cmp_ret = node->ip->compare(*((*head)->ip));
503 if (node_cmp_ret == SFIP_EQUAL)
504 {
505 sfip_node_free(node);
506 return SFIP_SUCCESS;
507 }
508 else if (node_cmp_ret == SFIP_LESSER)
509 {
510 node->next = *head;
511 *head = node;
512 ++*count;
513 return SFIP_SUCCESS;
514 }
515
516 /* If we're here, the head node was lesser than the new node */
517 /* Before searching the list, verify there is at least two nodes.
518 * (This saves an extra check during the loop below) */
519 if (!(*head)->next)
520 {
521 (*head)->next = node;
522 ++*count;
523 return SFIP_SUCCESS;
524 }
525
526 /* Insertion sort */
527 for (p = *head; p->next; p=p->next)
528 {
529 node_cmp_ret = node->ip->compare(*(p->next->ip));
530 if (node_cmp_ret == SFIP_EQUAL)
531 {
532 sfip_node_free(node);
533 return SFIP_SUCCESS;
534 }
535 else if (node_cmp_ret == SFIP_LESSER)
536 {
537 swp = p->next;
538 p->next = node;
539 node->next = swp;
540 ++*count;
541 return SFIP_SUCCESS;
542 }
543 }
544
545 p->next = node;
546 ++*count;
547
548 return SFIP_SUCCESS;
549 }
550
sfvar_create_alias(const sfip_var_t * alias_from,const char * alias_to)551 sfip_var_t* sfvar_create_alias(const sfip_var_t* alias_from, const char* alias_to)
552 {
553 sfip_var_t* ret;
554
555 if ((alias_from == nullptr) || (alias_to == nullptr))
556 return nullptr;
557
558 ret = sfvar_deep_copy(alias_from);
559 if (ret == nullptr)
560 return nullptr;
561
562 ret->name = snort_strdup(alias_to);
563 ret->id = alias_from->id;
564
565 return ret;
566 }
567
sfvar_is_alias(const sfip_var_t * one,const sfip_var_t * two)568 static int sfvar_is_alias(const sfip_var_t* one, const sfip_var_t* two)
569 {
570 if ((one == nullptr) || (two == nullptr))
571 return 0;
572
573 if ((one->id != 0) && (one->id == two->id))
574 return 1;
575 return 0;
576 }
577
sfvar_list_compare(sfip_node_t * list1,sfip_node_t * list2)578 static SfIpRet sfvar_list_compare(sfip_node_t* list1, sfip_node_t* list2)
579 {
580 sfip_node_t* tmp, * tmp2;
581
582 if ((list1 == nullptr) && (list2 == nullptr))
583 return SFIP_EQUAL;
584
585 /* Lists are ordered and of equal size */
586 for (tmp = list1, tmp2 = list2; (tmp != nullptr) && (tmp2 != nullptr); tmp = tmp->next,
587 tmp2 = tmp2->next)
588 {
589 if ((tmp->ip->compare(*(tmp2->ip)) != SFIP_EQUAL))
590 {
591 return SFIP_FAILURE;
592 }
593 }
594 return SFIP_EQUAL;
595 }
596
597 /* Check's if two variables have the same nodes */
sfvar_compare(const sfip_var_t * one,const sfip_var_t * two)598 SfIpRet sfvar_compare(const sfip_var_t* one, const sfip_var_t* two)
599 {
600 /* If both null, consider equal */
601 if (!one && !two)
602 return SFIP_EQUAL;
603
604 /* If one null and not the other, consider unequal */
605 if ((one && !two) || (!one && two))
606 return SFIP_FAILURE;
607
608 if (sfvar_is_alias(one, two))
609 return SFIP_EQUAL;
610
611 if (one->head_count != two->head_count)
612 return SFIP_FAILURE;
613
614 if (one->neg_head_count != two->neg_head_count)
615 return SFIP_FAILURE;
616
617 if (sfvar_list_compare(one->head, two->head) == SFIP_FAILURE)
618 return SFIP_FAILURE;
619
620 if (sfvar_list_compare(one->neg_head, two->neg_head) == SFIP_FAILURE)
621 return SFIP_FAILURE;
622
623 return SFIP_EQUAL;
624 }
625
626 /* Support function for sfvar_parse_iplist. Used to
627 * correctly match up end brackets.
628 * (Can't just do strchr(str, ']') because of the
629 * [a, [b], c] case, and can't do strrchr because
630 * of the [a, [b], [c]] case) */
_find_end_token(const char * str)631 static const char* _find_end_token(const char* str)
632 {
633 int stack = 0;
634
635 for (; *str; str++)
636 {
637 if (*str == LIST_OPEN)
638 stack++;
639 else if (*str == LIST_CLOSE)
640 stack--;
641
642 if (!stack)
643 {
644 return str;
645 }
646 }
647
648 return nullptr;
649 }
650
651 /* Support function for sfvar_parse_iplist.
652 * Negates a node */
_negate_node(sfip_node_t * node)653 static void _negate_node(sfip_node_t* node)
654 {
655 if (node->addr_flags & SFIP_NEGATED)
656 {
657 node->addr_flags &= ~SFIP_NEGATED;
658 node->flags &= ~SFIP_NEGATED;
659 }
660 else
661 {
662 node->addr_flags |= SFIP_NEGATED;
663 node->flags |= SFIP_NEGATED;
664 }
665 }
666
667 /* Support function for sfvar_parse_iplist.
668 * Negates a variable */
_negate_lists(sfip_var_t * var)669 static void _negate_lists(sfip_var_t* var)
670 {
671 sfip_node_t* node;
672 sfip_node_t* temp;
673 uint32_t temp_count;
674
675 for (node = var->head; node; node=node->next)
676 _negate_node(node);
677
678 for (node = var->neg_head; node; node=node->next)
679 _negate_node(node);
680
681 /* Swap lists */
682 temp = var->head;
683 var->head = var->neg_head;
684 var->neg_head = temp;
685
686 /*Swap the counts*/
687 temp_count = var->neg_head_count;
688 var->neg_head_count = var->head_count;
689 var->head_count = temp_count;
690 }
691
sfvar_parse_iplist(vartable_t * table,sfip_var_t * var,const char * str,int negation)692 SfIpRet sfvar_parse_iplist(vartable_t* table, sfip_var_t* var,
693 const char* str, int negation)
694 {
695 const char* end;
696 char* tok;
697 SfIpRet ret;
698 int neg_ip;
699
700 if (!var || !str)
701 return SFIP_ARG_ERR;
702
703 while (*str)
704 {
705 /* Skip whitespace and leading commas */
706 if (isspace((int)*str) || *str == ',')
707 {
708 str++;
709 continue;
710 }
711
712 neg_ip = 0;
713
714 /* Handle multiple negations */
715 for (; *str == '!' or isspace(*str); str++)
716 {
717 if ( *str == '!' )
718 neg_ip = !neg_ip;
719 }
720
721 if (!*str)
722 return SFIP_ARG_ERR;
723
724 /* Find end of this token */
725 for (end = str+1;
726 *end && !isspace((int)*end) && *end != LIST_CLOSE && *end != ',';
727 end++)
728 ;
729
730 tok = snort_strndup(str, end - str);
731
732 if (*str == LIST_OPEN)
733 {
734 char* list_tok;
735
736 /* Find end of this list */
737 if ((end = _find_end_token(str)) == nullptr)
738 {
739 /* No trailing bracket found */
740 snort_free(tok);
741 return SFIP_UNMATCHED_BRACKET;
742 }
743
744 str++;
745 list_tok = snort_strndup(str, end - str);
746
747 if ((ret = sfvar_parse_iplist(table, var, list_tok,
748 negation ^ neg_ip)) != SFIP_SUCCESS)
749 {
750 snort_free(list_tok);
751 snort_free(tok);
752 return ret;
753 }
754
755 snort_free(list_tok);
756 }
757 else if (*str == '$')
758 {
759 if (!table)
760 {
761 snort_free(tok);
762 return SFIP_LOOKUP_UNAVAILABLE;
763 }
764
765 sfip_var_t* tmp_var;
766 sfip_var_t* copy_var;
767
768 if ((tmp_var = sfvt_lookup_var(table, tok)) == nullptr)
769 {
770 snort_free(tok);
771 return SFIP_LOOKUP_FAILURE;
772 }
773
774 copy_var = sfvar_deep_copy(tmp_var);
775 /* Apply the negation */
776 if (negation ^ neg_ip)
777 {
778 /* Check for a negated "any" */
779 if (copy_var->head && copy_var->head->flags & SFIP_ANY)
780 {
781 snort_free(tok);
782 sfvar_free(copy_var);
783 return SFIP_NOT_ANY;
784 }
785
786 /* Check if this is a negated, zeroed IP (equivalent of a "!any") */
787 if (copy_var->head && !copy_var->head->ip->is_set())
788 {
789 snort_free(tok);
790 sfvar_free(copy_var);
791 return SFIP_NOT_ANY;
792 }
793
794 _negate_lists(copy_var);
795 }
796
797 sfvar_add(var, copy_var);
798 sfvar_free(copy_var);
799 }
800 else if (*str == LIST_CLOSE)
801 {
802 /* This should be the last character, if not, then this is an
803 * invalid extra closing bracket */
804 if (!(*(str+1)))
805 {
806 snort_free(tok);
807 return SFIP_SUCCESS;
808 }
809
810 snort_free(tok);
811 return SFIP_UNMATCHED_BRACKET;
812 }
813 else
814 {
815 sfip_node_t* node;
816
817 /* Check for a negated "any" */
818 if (negation ^ neg_ip && !strcasecmp(tok, "any"))
819 {
820 snort_free(tok);
821 return SFIP_NOT_ANY;
822 }
823
824 /* This should be an IP address!
825 Allocate new node for this string and add it to "ret" */
826 if ((node = sfipnode_alloc(tok, &ret)) == nullptr)
827 {
828 snort_free(tok);
829 return ret;
830 }
831
832 if (negation ^ neg_ip)
833 {
834 _negate_node(node);
835 }
836
837 /* Check if this is a negated, zeroed IP (equivalent of a "!any") */
838 if (!node->ip->is_set() && (node->flags & SFIP_NEGATED))
839 {
840 sfip_node_free(node);
841 snort_free(tok);
842 return SFIP_NOT_ANY;
843 }
844
845 ret = sfvar_add_node(var, node, negation ^ neg_ip);
846
847 if (ret != SFIP_SUCCESS )
848 {
849 snort_free(tok);
850 return ret;
851 }
852 }
853
854 snort_free(tok);
855 if (*end)
856 str = end + 1;
857 else
858 break;
859 }
860
861 return SFIP_SUCCESS;
862 }
863
sfvar_validate(sfip_var_t * var)864 SfIpRet sfvar_validate(sfip_var_t* var)
865 {
866 sfip_node_t* idx, * neg_idx;
867
868 if (!var->head || !var->neg_head)
869 return SFIP_SUCCESS;
870
871 for (idx = var->head; idx; idx = idx->next)
872 {
873 for (neg_idx = var->neg_head; neg_idx; neg_idx = neg_idx->next)
874 {
875 /* A smaller netmask means "less specific" */
876 if ((neg_idx->ip->get_bits() <= idx->ip->get_bits()) &&
877 /* Verify they overlap */
878 (neg_idx->ip->contains(idx->ip->get_addr()) == SFIP_CONTAINS))
879 {
880 return SFIP_CONFLICT;
881 }
882 }
883 }
884
885 return SFIP_SUCCESS;
886 }
887
888 /* Allocates and returns a new variable, described by "variable". */
sfvar_alloc(vartable_t * table,const char * variable,SfIpRet * status)889 sfip_var_t* sfvar_alloc(vartable_t* table, const char* variable, SfIpRet* status)
890 {
891 sfip_var_t* ret, * tmpvar;
892 const char* str, * end;
893 char* tmp;
894 SfIpRet stat;
895
896 if (!variable || !(*variable))
897 {
898 if (status)
899 *status = SFIP_ARG_ERR;
900 return nullptr;
901 }
902
903 if ( (ret = _alloc_var()) == nullptr )
904 {
905 if (status)
906 *status = SFIP_ALLOC_ERR;
907 return nullptr;
908 }
909
910 /* Extract and save the variable's name
911 Start by skipping leading whitespace or line continuations: '\' */
912 for (str = variable; *str && (isspace((int)*str) || *str == '\\'); str++)
913 ;
914 if (*str == 0) /* Didn't get anything */
915 {
916 if (status)
917 *status = SFIP_ARG_ERR;
918
919 sfvar_free(ret);
920 return nullptr;
921 }
922
923 /* Find the end of the name */
924 for (end = str; *end && !isspace((int)*end) && *end != '\\'; end++)
925 ;
926
927 if (!isalnum((int)*str) && *str != '$' && *str != '!')
928 {
929 if (status)
930 *status = SFIP_ARG_ERR;
931
932 sfvar_free(ret);
933 return nullptr;
934 }
935
936 /* Set the new variable's name/key */
937 if ((ret->name = snort_strndup(str, end - str)) == nullptr)
938 {
939 if (status)
940 *status = SFIP_ALLOC_ERR;
941
942 sfvar_free(ret);
943 return nullptr;
944 }
945
946 /* End points to the end of the name. Skip past it and any whitespace
947 * or potential line continuations */
948 str = end;
949 for (; (*str != 0) && (isspace((int)*str) || (*str == '\\')); str++)
950 ;
951 if (*str == 0) /* Didn't get anything */
952 {
953 if (status)
954 *status = SFIP_ARG_ERR;
955
956 sfvar_free(ret);
957 return nullptr;
958 }
959
960 /* Trim off whitespace and line continuations from the end of the string */
961 end = (str + strlen(str)) - 1;
962 for (; (end > str) && (isspace((int)*end) || (*end == '\\')); end--)
963 ;
964 end++;
965
966 /* See if this is just an alias */
967 tmp = snort_strndup(str, end - str);
968 tmpvar = sfvt_lookup_var(table, tmp);
969 snort_free(tmp);
970 if (tmpvar != nullptr)
971 {
972 sfip_var_t* aliased = sfvar_create_alias(tmpvar, ret->name);
973 if (aliased != nullptr)
974 {
975 if (status != nullptr)
976 *status = SFIP_SUCCESS;
977
978 sfvar_free(ret);
979 return aliased;
980 }
981 }
982
983 /* Everything is treated as a list, even if it's one element that's not
984 * surrounded by brackets */
985 stat = sfvar_parse_iplist(table, ret, str, 0);
986 if (status != nullptr)
987 *status = stat;
988
989 if (stat != SFIP_SUCCESS)
990 {
991 sfvar_free(ret);
992 return nullptr;
993 }
994
995 if (ret->head &&
996 (ret->head->flags & SFIP_ANY && ret->head->flags & SFIP_NEGATED))
997 {
998 if (status)
999 *status = SFIP_NOT_ANY;
1000
1001 sfvar_free(ret);
1002 return nullptr;
1003 }
1004
1005 if (sfvar_validate(ret) == SFIP_CONFLICT)
1006 {
1007 if (status)
1008 *status = SFIP_CONFLICT;
1009
1010 sfvar_free(ret);
1011 return nullptr;
1012 }
1013
1014 return ret;
1015 }
1016
1017 /* Support function for sfvar_ip_in */
sfvar_ip_in4(sfip_var_t * var,const SfIp * ip)1018 static inline bool sfvar_ip_in4(sfip_var_t* var, const SfIp* ip)
1019 {
1020 int match;
1021 sfip_node_t* pos_idx, * neg_idx;
1022
1023 match = 0;
1024
1025 pos_idx = var->head;
1026 neg_idx = var->neg_head;
1027
1028 if (!pos_idx)
1029 {
1030 for (; neg_idx; neg_idx = neg_idx->next)
1031 {
1032 if (neg_idx->ip->get_addr()->get_family() != AF_INET)
1033 continue;
1034
1035 if (neg_idx->ip->fast_cont4(*ip))
1036 return false;
1037 }
1038
1039 return true;
1040 }
1041
1042 while (pos_idx)
1043 {
1044 if (neg_idx)
1045 {
1046 if (neg_idx->ip->get_addr()->get_family() == AF_INET &&
1047 neg_idx->ip->fast_cont4(*ip))
1048 {
1049 return false;
1050 }
1051
1052 neg_idx = neg_idx->next;
1053 }
1054 /* No more potential negations. Check if we've already matched. */
1055 else if (match)
1056 {
1057 return true;
1058 }
1059
1060 if (!match)
1061 {
1062 if (pos_idx->ip->is_set())
1063 {
1064 if (pos_idx->ip->get_addr()->get_family() == AF_INET &&
1065 pos_idx->ip->fast_cont4(*ip))
1066 {
1067 match = 1;
1068 }
1069 else
1070 {
1071 pos_idx = pos_idx->next;
1072 }
1073 }
1074 else
1075 {
1076 match = 1;
1077 }
1078 }
1079 }
1080
1081 return false;
1082 }
1083
1084 /* Support function for sfvar_ip_in */
sfvar_ip_in6(sfip_var_t * var,const SfIp * ip)1085 static inline bool sfvar_ip_in6(sfip_var_t* var, const SfIp* ip)
1086 {
1087 int match;
1088 sfip_node_t* pos_idx, * neg_idx;
1089
1090 match = 0;
1091
1092 pos_idx = var->head;
1093 neg_idx = var->neg_head;
1094
1095 if (!pos_idx)
1096 {
1097 for (; neg_idx; neg_idx = neg_idx->next)
1098 {
1099 if (neg_idx->ip->get_addr()->get_family() != AF_INET6)
1100 continue;
1101
1102 if (neg_idx->ip->fast_cont6(*ip))
1103 return false;
1104 }
1105
1106 return true;
1107 }
1108
1109 while (pos_idx)
1110 {
1111 if (neg_idx)
1112 {
1113 if (neg_idx->ip->get_addr()->get_family() == AF_INET6 &&
1114 neg_idx->ip->fast_cont6(*ip))
1115 {
1116 return false;
1117 }
1118
1119 neg_idx = neg_idx->next;
1120 }
1121 /* No more potential negations. Check if we've already matched. */
1122 else if (match)
1123 {
1124 return true;
1125 }
1126
1127 if (!match)
1128 {
1129 if (pos_idx->ip->is_set())
1130 {
1131 if (pos_idx->ip->get_addr()->get_family() == AF_INET6 &&
1132 pos_idx->ip->fast_cont6(*ip))
1133 {
1134 match = 1;
1135 }
1136 else
1137 {
1138 pos_idx = pos_idx->next;
1139 }
1140 }
1141 else
1142 {
1143 match = 1;
1144 }
1145 }
1146 }
1147
1148 return false;
1149 }
1150
sfvar_ip_in(sfip_var_t * var,const SfIp * ip)1151 bool sfvar_ip_in(sfip_var_t* var, const SfIp* ip)
1152 {
1153 if (!var || !ip)
1154 return false;
1155
1156 /* Since this is a performance-critical function it uses different
1157 * codepaths for IPv6 and IPv4 traffic, rather than the dual-stack
1158 * functions. */
1159
1160 if (ip->get_family() == AF_INET)
1161 {
1162 return sfvar_ip_in4(var, ip);
1163 }
1164 else
1165 {
1166 return sfvar_ip_in6(var, ip);
1167 }
1168 }
1169
1170 #ifdef UNIT_TEST
1171 #define SFIPVAR_TEST_BUFF_LEN 512
1172 static char sfipvar_test_buff[SFIPVAR_TEST_BUFF_LEN];
print_var_list(sfip_node_t * var_list,bool print_bits=false)1173 static void print_var_list(sfip_node_t* var_list, bool print_bits = false)
1174 {
1175 int n = 0;
1176 for (sfip_node_t* p = var_list; p; p = p->next)
1177 {
1178 if (p->flags & SFIP_ANY)
1179 n += safe_snprintf(sfipvar_test_buff+n, SFIPVAR_TEST_BUFF_LEN - n, "any");
1180 else if (p->flags & SFIP_NEGATED)
1181 {
1182 SfIpString ip_str;
1183 n += safe_snprintf(sfipvar_test_buff+n, SFIPVAR_TEST_BUFF_LEN - n, "!%s",p->ip->ntop(ip_str));
1184 }
1185 else
1186 {
1187 SfIpString ip_str;
1188 n += safe_snprintf(sfipvar_test_buff+n, SFIPVAR_TEST_BUFF_LEN - n, "%s", p->ip->ntop(ip_str));
1189 }
1190
1191 if (print_bits and !(p->flags & SFIP_ANY))
1192 n += safe_snprintf(sfipvar_test_buff+n, SFIPVAR_TEST_BUFF_LEN - n, "/%d",
1193 p->ip->get_bits());
1194
1195 if (p->next)
1196 n += safe_snprintf(sfipvar_test_buff+n, SFIPVAR_TEST_BUFF_LEN - n, ",");
1197 }
1198 }
1199
1200 TEST_CASE("SfIpVarListMerge", "[SfIpVar]")
1201 {
1202 vartable_t* table;
1203 sfip_var_t* var1;
1204 sfip_var_t* var2;
1205
1206 SfIp::test_features = true;
1207
1208 SECTION("basic list merge")
1209 {
1210 table = sfvt_alloc_table();
1211 CHECK(sfvt_add_str(table, "foo [ 192.168.0.1, 192.168.5.0, 192.168.0.2, 255.255.248.0 ] ",
1212 &var1) == SFIP_SUCCESS);
1213 CHECK(sfvt_add_str(table, "goo [ 255.255.241.0, 192.168.2.1] ", &var2) == SFIP_SUCCESS);
1214 print_var_list(var1->head);
1215 CHECK(!strcmp("192.168.0.1,192.168.0.2,192.168.5.0,255.255.248.0", sfipvar_test_buff));
1216 print_var_list(var2->head);
1217 CHECK(!strcmp("192.168.2.1,255.255.241.0", sfipvar_test_buff));
1218
1219 // add list var2 to list var1, a merge_list() will be called
1220 CHECK(SFIP_SUCCESS == sfvar_add(var1, var2));
1221 print_var_list(var1->head);
1222 // the merged list should be sorted as well
1223 CHECK(!strcmp("192.168.0.1,192.168.0.2,192.168.2.1,192.168.5.0,255.255.241.0,255.255.248.0",
1224 sfipvar_test_buff));
1225 // address contains variable, it will merge the list from $variable
1226 CHECK(sfvt_add_str(table, "moo [ $goo, 192.168.6.1] ", &var2) == SFIP_SUCCESS);
1227 print_var_list(var2->head);
1228 CHECK(!strcmp("192.168.2.1,192.168.6.1,255.255.241.0", sfipvar_test_buff));
1229
1230 // add CIDR addresses
1231 CHECK(sfvt_add_str(table, "my_cidr [ 192.168.0.0/16, f0:e0:d0:c0::8/64, 10.10.1.8/19,"
1232 " f0:e0:d1:c1::1/32]", &var2) == SFIP_SUCCESS);
1233 print_var_list(var2->head);
1234
1235 CHECK(!strcmp("10.10.0.0,192.168.0.0,00f0:00e0:0000:0000:0000:0000:0000:0000",
1236 sfipvar_test_buff));
1237
1238 // merge the list
1239 CHECK(SFIP_SUCCESS == sfvar_add(var2, var1));
1240 print_var_list(var2->head);
1241 CHECK(!strcmp("10.10.0.0,192.168.0.0,255.255.241.0,255.255.248.0,"
1242 "00f0:00e0:0000:0000:0000:0000:0000:0000", sfipvar_test_buff));
1243
1244 // merge the list again; should yield the same result
1245 CHECK(SFIP_SUCCESS == sfvar_add(var1, var2));
1246 print_var_list(var1->head);
1247 CHECK(!strcmp("10.10.0.0,192.168.0.0,255.255.241.0,255.255.248.0,"
1248 "00f0:00e0:0000:0000:0000:0000:0000:0000", sfipvar_test_buff));
1249
1250 sfvt_free_table(table);
1251 }
1252
1253 SECTION("merge related IPs")
1254 {
1255 table = sfvt_alloc_table();
1256 //[ 192.168.255.1/20, 192.168.255.2 ] with [ 192.168.255.1/21, 192.168.255.2/31 ]
1257 CHECK(sfvt_add_str(table, "ip11 [ 192.168.255.1/20, 192.168.255.2]", &var1)
1258 == SFIP_SUCCESS);
1259 CHECK(sfvt_add_str(table, "ip12 [192.168.255.1/21, 192.168.255.2/31]", &var2)
1260 == SFIP_SUCCESS);
1261 CHECK(SFIP_SUCCESS == sfvar_add(var1, var2));
1262 print_var_list(var1->head, true);
1263 CHECK(!strcmp("192.168.240.0/116", sfipvar_test_buff));
1264
1265 //[ 1.2.3.8, 1.2.3.9, 1.2.3.10 ] with [ 1.2.3.255/25 ]
1266 CHECK(sfvt_add_str(table, "ip21 [ 1.2.3.8, 1.2.3.9, 1.2.3.10]", &var1) == SFIP_SUCCESS);
1267 CHECK(sfvt_add_str(table, "ip22 [11.2.3.255/25 ]", &var2) == SFIP_SUCCESS);
1268 CHECK(SFIP_SUCCESS == sfvar_add(var1, var2));
1269 print_var_list(var1->head, true);
1270 CHECK(!strcmp("1.2.3.8/128,1.2.3.9/128,1.2.3.10/128,11.2.3.128/121", sfipvar_test_buff));
1271
1272 //[ 10.9.8.7 ] with [ !10.9.8.7 ]
1273 CHECK(sfvt_add_str(table, "ip31 [ 10.9.8.7 ]", &var1) == SFIP_SUCCESS);
1274 CHECK(sfvt_add_str(table, "ip32 [ !10.9.8.7 ]", &var2) == SFIP_SUCCESS);
1275 CHECK(SFIP_SUCCESS == sfvar_add(var1, var2));
1276 print_var_list(var1->head, true);
1277 CHECK(!strcmp("10.9.8.7/128", sfipvar_test_buff));
1278 print_var_list(var1->neg_head, true);
1279 CHECK(!strcmp("!10.9.8.7/128", sfipvar_test_buff));
1280
1281 // Duplicate ip in lists of a single ip
1282 CHECK(sfvt_add_str(table, "once [ 1.1.1.1/16 ]", &var1)
1283 == SFIP_SUCCESS);
1284 CHECK(sfvt_add_str(table, "again [ 1.1.1.1/16 ]", &var2)
1285 == SFIP_SUCCESS);
1286 CHECK(SFIP_SUCCESS == sfvar_add(var1, var2));
1287 print_var_list(var1->head);
1288 CHECK(!strcmp("1.1.0.0", sfipvar_test_buff));
1289
1290 // Subset of ip in lists of a single ip
1291 CHECK(sfvt_add_str(table, "single [ 1.1.1.1 ]", &var1)
1292 == SFIP_SUCCESS);
1293 CHECK(sfvt_add_str(table, "similar [ 1.1.1.1/16 ]", &var2)
1294 == SFIP_SUCCESS);
1295 CHECK(SFIP_SUCCESS == sfvar_add(var1, var2));
1296 print_var_list(var1->head);
1297 CHECK(!strcmp("1.1.0.0", sfipvar_test_buff));
1298 sfvt_free_table(table);
1299 }
1300
1301 SECTION("merge contained IPs and negated-IPs")
1302 {
1303
1304 SfIp::test_features = true;
1305
1306 table = sfvt_alloc_table();
1307
1308 CHECK(sfvt_add_str(table, "foo 1.2.3.4, cafe:feed:beef::0/48", &var1) == SFIP_SUCCESS);
1309 CHECK(sfvt_add_to_var(table, var1, "1.2.3.5/24, cafe:feed::0/16") == SFIP_SUCCESS);
1310 CHECK(sfvt_add_to_var(table, var1, "!9.8.7.6, !dead:beef:abcd::5") == SFIP_SUCCESS);
1311 CHECK(sfvt_add_to_var(table, var1, "!9.8.5.4/8, cafe:feed:abcd::0") == SFIP_SUCCESS);
1312 CHECK(sfvt_add_to_var(table, var1, "!9.8.3.2, 1.2.6.7/16") == SFIP_SUCCESS);
1313 CHECK(sfvt_add_to_var(table, var1, "1.2.3.4/32, cafe:feed::0") == SFIP_SUCCESS);
1314 CHECK(sfvt_add_to_var(table, var1, "!dead:beef:abcd::0/32") == SFIP_SUCCESS);
1315
1316 /* Check merged IP lists */
1317 print_var_list(var1->head);
1318
1319 CHECK(!strcmp("1.2.0.0,cafe:0000:0000:0000:0000:0000:0000:0000", sfipvar_test_buff));
1320 print_var_list(var1->neg_head);
1321 CHECK(!strcmp("!9.0.0.0,!dead:beef:0000:0000:0000:0000:0000:0000", sfipvar_test_buff));
1322
1323 /* Check lookups */
1324 SfIp* ip = (SfIp *)snort_alloc(sizeof(SfIp));
1325 CHECK(ip->set("9.8.3.2") == SFIP_SUCCESS);
1326 CHECK((sfvar_ip_in(var1, ip) == false));
1327 snort_free(ip);
1328
1329 ip = (SfIp *)snort_alloc(sizeof(SfIp));
1330 uint16_t srcBits;
1331 CHECK(ip->set("1.2.3.4/24", &srcBits) == SFIP_SUCCESS);
1332 CHECK((sfvar_ip_in(var1, ip) == true));
1333 snort_free(ip);
1334
1335 ip = (SfIp *)snort_alloc(sizeof(SfIp));
1336 CHECK(ip->set("dead:beef::0") == SFIP_SUCCESS);
1337 CHECK((sfvar_ip_in(var1, ip) == false));
1338 snort_free(ip);
1339
1340 ip = (SfIp *)snort_alloc(sizeof(SfIp));
1341 CHECK(ip->set("cafe:abcd::9999") == SFIP_SUCCESS);
1342 CHECK((sfvar_ip_in(var1, ip) == true));
1343 snort_free(ip);
1344
1345 sfvt_free_table(table);
1346 }
1347
1348 SECTION("merge without table")
1349 {
1350 SfIp* ip;
1351
1352 var2 = (sfip_var_t*)snort_calloc(sizeof(sfip_var_t));
1353 table = sfvt_alloc_table();
1354
1355 // 'foo' variable
1356 CHECK(sfvt_add_str(table, "foo 1.0.0.1", &var1) == SFIP_SUCCESS);
1357
1358 // no table used
1359 CHECK(sfvt_add_to_var(nullptr, var2, "1.0.0.2") == SFIP_SUCCESS);
1360 CHECK(sfvt_add_to_var(nullptr, var2, "$foo") == SFIP_LOOKUP_UNAVAILABLE);
1361 CHECK(sfvt_add_to_var(nullptr, var2, "$moo") == SFIP_LOOKUP_UNAVAILABLE);
1362
1363 print_var_list(var2->head);
1364 CHECK(!strcmp("1.0.0.2", sfipvar_test_buff));
1365
1366 ip = (SfIp *)snort_alloc(sizeof(SfIp));
1367 CHECK(ip->set("1.0.0.1") == SFIP_SUCCESS);
1368 CHECK((sfvar_ip_in(var2, ip) == false));
1369 snort_free(ip);
1370
1371 ip = (SfIp *)snort_alloc(sizeof(SfIp));
1372 CHECK(ip->set("1.0.0.2") == SFIP_SUCCESS);
1373 CHECK((sfvar_ip_in(var2, ip) == true));
1374 snort_free(ip);
1375
1376 // using table
1377 CHECK(sfvt_add_to_var(table, var2, "$foo") == SFIP_SUCCESS);
1378 CHECK(sfvt_add_to_var(table, var2, "$moo") == SFIP_LOOKUP_FAILURE);
1379
1380 print_var_list(var2->head);
1381 CHECK(!strcmp("1.0.0.1,1.0.0.2", sfipvar_test_buff));
1382
1383 ip = (SfIp *)snort_alloc(sizeof(SfIp));
1384 CHECK(ip->set("1.0.0.1") == SFIP_SUCCESS);
1385 CHECK((sfvar_ip_in(var2, ip) == true));
1386 snort_free(ip);
1387
1388 ip = (SfIp *)snort_alloc(sizeof(SfIp));
1389 CHECK(ip->set("1.0.0.2") == SFIP_SUCCESS);
1390 CHECK((sfvar_ip_in(var2, ip) == true));
1391 snort_free(ip);
1392
1393 sfvar_free(var2);
1394 sfvt_free_table(table);
1395 }
1396 }
1397
1398 TEST_CASE("SfIpVarCopyAddCompare", "[SfIpVar]")
1399 {
1400 vartable_t* table;
1401 sfip_var_t* var1;
1402 sfip_var_t* var2;
1403 sfip_node_t* node;
1404
1405 table = sfvt_alloc_table();
1406 CHECK(sfvt_add_str(table, "foo [ 192.168.0.1, 192.168.5.0, 192.168.0.2, 255.255.248.0 ] ",
1407 &var1) == SFIP_SUCCESS);
1408 print_var_list(var1->head);
1409 CHECK(!strcmp("192.168.0.1,192.168.0.2,192.168.5.0,255.255.248.0", sfipvar_test_buff));
1410 // deep copy the list
1411 var2 = sfvar_deep_copy(var1);
1412 // compare to original
1413 CHECK(SFIP_EQUAL == sfvar_compare(var1, var2));
1414
1415 // add a negate node to original list
1416 node = sfipnode_alloc("!192.168.3.2", nullptr);
1417 CHECK(node != nullptr);
1418 CHECK(SFIP_SUCCESS == sfvar_add_node(var1, node, 1));
1419 print_var_list(var1->neg_head);
1420 CHECK(!strcmp("!192.168.3.2", sfipvar_test_buff));
1421 // now compare should fail
1422 CHECK(SFIP_FAILURE == sfvar_compare(var1, var2));
1423
1424 // add a node
1425 node = sfipnode_alloc("192.168.90.9", nullptr);
1426 CHECK(node != nullptr);
1427 CHECK(SFIP_SUCCESS == sfvar_add_node(var1, node, 0));
1428 print_var_list(var1->head);
1429 CHECK(!strcmp("192.168.0.1,192.168.0.2,192.168.5.0,192.168.90.9,255.255.248.0",
1430 sfipvar_test_buff));
1431
1432 sfvar_free(var2);
1433 sfvt_free_table(table);
1434 }
1435
1436 TEST_CASE("SfIpVarAny", "[SfIpVar]")
1437 {
1438 vartable_t* table;
1439 sfip_var_t* var1;
1440 sfip_var_t* var2;
1441 sfip_node_t* node;
1442
1443 table = sfvt_alloc_table();
1444
1445 CHECK(sfvt_add_str(table, "foo [any] ", &var1) == SFIP_SUCCESS);
1446 print_var_list(var1->head);
1447 CHECK(!strcmp("any", sfipvar_test_buff));
1448
1449 // try to add list to any
1450 CHECK(sfvt_add_str(table, "goo [ 255.255.241.0, 192.168.2.1] ", &var2) == SFIP_SUCCESS);
1451 CHECK(SFIP_SUCCESS == sfvar_add(var1, var2));
1452 // adding something to any should not change any
1453 print_var_list(var1->head);
1454 CHECK(!strcmp("any", sfipvar_test_buff));
1455
1456 // create a list and add any to it
1457 node = sfipnode_alloc("any", nullptr);
1458 CHECK(node != nullptr);
1459 CHECK(SFIP_SUCCESS == sfvar_add_node(var1, node, 0));
1460
1461 // after adding any, the original list should have any only
1462 print_var_list(var1->head);
1463 CHECK(!strcmp("any", sfipvar_test_buff));
1464 CHECK(var1->head_count == 1);
1465
1466 sfvt_free_table(table);
1467 }
1468
1469 #endif
1470
1471