snort 中的IP集合解析

簡介

    前面總結了snort在讀取配置文件時的整體流程,這裏選擇較爲重要且相對有價值的細節部分進行分析,snort的IP串解析方式.
node

    由於snort支持[ ]造成IP集合而且支持使用'!'取反,所以可能構成以下的串 ![IP, !IP ![IP, !IP]]。
學習

    所以會增長解析的難度.
ui

分析

    snort處理這個問題的思想是,當發現一個 '['符號時便尋找與之對應的']'並將之間的串進行遞歸的解析,這樣就能處理層次問題了.
this

代碼分析

    如下是snort-2.9.6.0中多個代碼片斷合併而成
spa

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集合直接採用深度拷貝過來code

  3. 對於第二點的深度拷貝目前緣由不明,可能方便後面從新整理構建,但若是不是的話,副本可使用相似智能指針的思想來建立遞歸

相關文章
相關標籤/搜索