简介
前面总结了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集合直接采用深度拷贝过来
对于第二点的深度拷贝目前原因不明,可能方便后面重新整理构建,但如果不是的话,副本可以使用类似智能指针的思想来创建