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