snort 中的IP集合解析

简介

    前面总结了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;
}

总结

  1. 该段代码在括号处理得技巧上值得学习

  2. 还有一处从这部分代码片段看不太明显地方;是对解析过一次的字符串,再次遇到相同的串就将其解析后的IP集合直接采用深度拷贝过来

  3. 对于第二点的深度拷贝目前原因不明,可能方便后面重新整理构建,但如果不是的话,副本可以使用类似智能指针的思想来创建


转载于:https://my.oschina.net/u/572632/blog/289745

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值