前面总结了snort在读取配置文件时的总体流程,这里选择较为重要且相对有价值的细节部分进行分析,snort的IP串解析方式.
因为snort支持[ ]形成IP集合并且支持使用'!'取反,因此可能构成如下的串 ![IP, !IP ![IP, !IP]]。
因此会增加解析的难度.
snort处理这个问题的思想是,当发现一个 '['符号时便寻找与之对应的']'并将之间的串进行递归的解析,这样就能处理层次问题了.
以下是snort-2.9.6.0中多个代码片段合并而成
typedef struct _ip { int16_t family; int16_t bits; /* see sfip_size(): these address bytes * must be the last field in this struct */ union { u_int8_t u6_addr8[16]; u_int16_t u6_addr16[8]; u_int32_t u6_addr32[4]; // u_int64_t u6_addr64[2]; } ip; #define ip8 ip.u6_addr8 #define ip16 ip.u6_addr16 #define ip32 ip.u6_addr32 // #define ip64 ip.u6_addr64 } sfip_t; /***********************************************************************/ #ifndef SF_IPVAR_H #define SF_IPVAR_H /* Flags */ #define SFIP_NEGATED 1 #define SFIP_ANY 2 #include <stdio.h> #include "sf_ip.h" /* Selects which mode a given variable is using to * store and lookup IP addresses */ typedef enum _modes { SFIP_LIST, SFIP_TABLE } MODES; /* Used by the "list" mode. A doubly linked list of sfip_t objects. */ typedef struct _ip_node { sfip_t *ip; #define ip_addr ip; /* To ease porting Snort */ struct _ip_node *next; int flags; // XXX int addr_flags; /* Flags used exlusively by Snort */ /* Keeping these variables seperate keeps * this from stepping on Snort's toes. */ /* Should merge them later */ } sfip_node_t; /* An IP variable onkect */ typedef struct _var_t { /* Selects whether or not to use the list, the table, * or any other method added later */ MODES mode; /* Linked lists. Switch to something faster later */ sfip_node_t *head; sfip_node_t *neg_head; /* The mode above will select whether to use the sfip_node_t linked list * or the IP routing table */ // sfrt rt; /* Linked list of IP variables for the variable table */ struct _var_t *next; uint32_t id; char *name; char *value; } sfip_var_t; /* A variable table for storing and looking up variables */ /* Expand later to use a faster data structure */ typedef struct _vartable_t { sfip_var_t *head; uint32_t id; } vartable_t; /***********************************************************************/ SFIP_RET sfvar_parse_iplist(vartable_t *table, sfip_var_t *var, char *str, int negation) { char *end, *tok; SFIP_RET ret; int neg_ip; if(!var || !table || !str) /**检查防止段错误*/ return SFIP_ARG_ERR; while(*str) { /* Skip whitespace and leading commas */ /** 跳过IP字符串前面的空指针以及多余的,*/ if(isspace((int)*str) || *str == ',') { str++; continue; } /**neg IP标志是为了标志如 !192.168.1.1这样的IP*/ neg_ip = 0; /* Handle multiple negations */ /**基数个 '!'为真, 偶数个'!'为否*/ for(; *str == '!'; str++) neg_ip = !neg_ip; /**提取一个IP串,这里可能会附带少量多余符号*/ /* Find end of this token */ for(end = str+1; *end && !isspace((int)*end) && *end != LIST_CLOSE && *end != ','; end++) ; /**拷贝我们提取出的串*/ tok = SnortStrndup(str, end - str); /**检查是从[exp1,exp2]中提取出 [exp1 这样的串*/ if(*str == LIST_OPEN) { char *list_tok; /**找到与开头的 '['对应的 ']'*/ if((end = _find_end_token(str)) == NULL) { /* No trailing bracket found */ free(tok); return SFIP_UNMATCHED_BRACKET; } str++; /**从 [exp] 中提取出了整个exp, exp可能是复数个IP条目*/ list_tok = SnortStrndup(str, end - str); /**递归拆分其中的子串, 注意negtive^neg_ip 完成了多重否定的解析*/ if((ret = sfvar_parse_iplist(table, var, list_tok, negation ^ neg_ip)) != SFIP_SUCCESS) { free(list_tok); free(tok); return ret; } free(list_tok); } /**这里通过递归已经将整个串拆分到最小单元即$VAR 或 !192.168.1.1这样的结构*/ else if(*str == '$') { /**该串是一个变量*/ sfip_var_t *tmp_var; sfip_var_t *copy_var; /**查看是否有这个变量*/ if((tmp_var = sfvt_lookup_var(table, tok)) == NULL) { /**未知变量,错误*/ free(tok); return SFIP_LOOKUP_FAILURE; } /**将该变量对应的IP串拷贝出来*/ copy_var = sfvar_deep_copy(tmp_var); /* Apply the negation */ /**确定该串的符号, 1标识为取反*/ if(negation ^ neg_ip) { /* Check for a negated "any" */ /**any 取反视为错误*/ if(copy_var->head && copy_var->head->flags & SFIP_ANY) { free(tok); sfvar_free(copy_var); return SFIP_NOT_ANY; } /**对0的IP取反视为错误*/ /* Check if this is a negated, zero'ed IP (equivalent of a "!any") */ if(copy_var->head && !sfip_is_set(copy_var->head->ip)) { free(tok); sfvar_free(copy_var); return SFIP_NOT_ANY; } /**反转数据, 即将 IP取反的集合与不取反的交换*/ _negate_lists(copy_var); } /**将该集合加入,该改则的IP集合*/ sfvar_add(var, copy_var); sfvar_free(copy_var); } else if(*str == LIST_CLOSE) { /* This should be the last character, if not, then this is an * invalid extra closing bracket */ if(!(*(str+1))) { free(tok); return SFIP_SUCCESS; } free(tok); return SFIP_UNMATCHED_BRACKET; } else { sfip_node_t *node; /* Skip leading commas */ for(; *str == ','; str++) ; /* Check for a negated "any" */ if(negation ^ neg_ip && !strcasecmp(tok, "any")) { free(tok); return SFIP_NOT_ANY; } /**普通的IP串,直接解析后添加*/ /* This should be an IP address! */ /* Allocate new node for this string and add it to "ret" */ if((node = sfipnode_alloc(tok, &ret)) == NULL) { free(tok); return ret; } if(negation ^ neg_ip) { _negate_node(node); } /* Check if this is a negated, zero'ed IP (equivalent of a "!any") */ if(!sfip_is_set(node->ip) && (node->flags & SFIP_NEGATED)) { sfip_node_free(node); free(tok); return SFIP_NOT_ANY; } ret = sfvar_add_node(var, node, negation ^ neg_ip); if(ret != SFIP_SUCCESS ) { free(tok); return ret; } } free(tok); /**只要该串解析完后不是末尾就将解析指针指向下一个*/ if(*end) str = end + 1; else break; } return SFIP_SUCCESS; } /***********************************************************************/ /* Support function for sfvar_parse_iplist. Used to * correctly match up end brackets. * (Can't just do strchr(str, ']') because of the * [a, [b], c] case, and can't do strrchr because * of the [a, [b], [c]] case) */ /**改代码非常精妙的找到了与第一个'['对应的 ']',而且同时完成了校验工作*/ static char *_find_end_token(char *str) { int stack = 0; for(; *str; str++) { if(*str == LIST_OPEN) stack++; else if(*str == LIST_CLOSE) stack--; if(!stack) { return str; } } return NULL; }
该段代码在括号处理得技巧上值得学习
还有一处从这部分代码片段看不太明显地方;是对解析过一次的字符串,再次遇到相同的串就将其解析后的IP集合直接采用深度拷贝过来
对于第二点的深度拷贝目前原因不明,可能方便后面重新整理构建,但如果不是的话,副本可以使用类似智能指针的思想来创建