www.pudn.com > Firewall_PNE_3_3.zip > fwHttpFilterLib.c, change:2009-03-16,size:33693b


/* fwHttpFilterLib.c - Firewall HTTP Content Filtering Extension module */

/*
 * Copyright (c) 2005-2006 Wind River Systems, Inc.
 *
 * The right to copy, distribute, modify or otherwise make use
 * of this software may be licensed only pursuant to the terms
 * of an applicable Wind River license agreement.
 */

/*
modification history
--------------------
01d,03mar06,kch  Fixed coverity issues in httpPktLocate() (SPR#118475).
01c,04feb06,kch  Fixed apigen doc errors and warnings.
01b,28sep05,zhu  removed demo code
01a,03aug05,myz  written
*/

/*
DESCRIPTION

This library provides a rudimentary content filter for the HTTP packet.It 
performs the URL filtering, proxy filtering, cookie blocking, Java applet and
ActiveX control filtering actions. The URL filtering is done based on the 
user specified URL database. Each entry of the database can specify either a 
URL path or a URL keyword to match. The proxy filtering matches all the packets
which have absolute URI in its request line, i.e. the URI has the form: 
http://xxx.xxx.xxx/xxx.  The cookie filtering replaces the cookie with the same
length of the meaningless data in the request packet. It does not prevent the
cookie from being set to your host. It simply prevents the cookie from being
transmitted back to the server. Java applet and ActiveX control filterings 
simply block the requests for the files with extension .zip, .class, .jar; 
and .ocx,.cab respectively. It does not inspect the HTML OBJECT or APPLET tags
or the signature of the object binary content. 
This content filter utilizes the firewall extension handler service 
registration mechanism to allow user add/remove the filtering functions 
described above dynamically.  The content filter operates in the context of 
the firewall extension handler installed on a firewall rule intercepting the 
outbound HTTP traffic. 

usage example:

    void * grpId;
    void * svcHdl;
    void * pUrlDesc;
                                                                                   
    grpId = fwRuleGroupCreate(FW_OUT_LOC,"test content filter",40);
    fwRuleFieldSet(grpId,FW_FIELD_TCP,0,0,80,80,0,0,0);
    fwRuleFieldSet(grpId,FW_FIELD_ACTION,FW_USER_ACTION);
    svcHdl =   fwExtSvcCreate();
    pUrlDesc = fwUrlListCreate();
    fwExtSvcReg(svcHdl,"URL filter",fwUrlFilter,(void *)pUrlDesc,FW_REJECT);
    fwExtSvcReg(svcHdl,"Filter Proxy",fwProxyFilter,NULL,FW_REJECT);
    fwExtSvcReg(svcHdl,"Block Java Applet",fwJavaAppletFilter,NULL,
                         FW_REJECT);
    fwExtSvcReg(svcHdl,"Block Activex",fwActiveXFilter,NULL,
                         FW_REJECT);
    fwExtSvcReg(svcHdl,"Cookie Block",fwCookieFilter,NULL,0);

    fwExtHandlerInstall(grpId,NULL,fwExtSvcProcess,svcHdl,NULL);

INCLUDE FILES: fwLib.h

*/


#include "fw.h"
#include "ctype.h"
#include "strSearchLib.h"

/* debug macros */

#define FW_HTTP_CTNT_DEBUG

#ifdef FW_HTTP_CTNT_DEBUG

int fwHttpCtntDebug = 0;
#define DBG_PRINT(X) \
    do { \
    if (fwHttpCtntDebug) \
        printf X; \
    }while(0)
#else

#define DBG_PRINT(X)

#endif

/* Local definitions */

#define INSPECTED_SIZE_MAX 1500
#define ONE_SPACE       1
#define TOKEN_GET       "GET"
#define TOKEN_HTTP      "http"
#define TOKEN_HOST      "Host"
#define HTTP_S2H_LEN    7  /* Scheme To Host length: http://  */
#define IS_STRING_SAME(pStr,strId) (bcmp((pStr),strId,strlen(strId)) == 0)
#define NEXT_FIELD_POS(curToken)  (strlen(curToken) + ONE_SPACE)
#define SKIP_SPACES(pStr)     while (*(char *)(pStr) == ' ') \
                                (char *)(pStr)++

/* string terminator and match types */

#define STR_GENERIC_TMNATOR(ch) ((ch) == '/' || (ch) == '\r' || (ch) == 0)
#define TMNATOR_TYPE(t)        ((t) & 0xff)
#define TMNATOR_MATCH(t)       (((t) >> 8) & 0xff)
#define TMNATOR_TYPE_SPECIFIC  1
#define TMNATOR_TYPE_GENERIC   2
#define LOOSE_MATCH            0x10   
#define STRICT_MATCH           0x20
#define FILE_EXT_MAXLEN        10

/* URL filter type */

#define URL_CLASS_INDIVIDUAL   1    /* filter specific URLs */
#define URL_CLASS_ABS_URI      2    /* filter absolute URI URLs */

/* object filter type */

#define OBJ_FILTER_JAVA        1    /* Java object filter type */
#define OBJ_FILTER_ACTIVEX     2    /* ActiveX control filter type */

/* local typedefs */

/* URL node */

typedef struct {
    DL_NODE node;
    char * pUrl; /* URL string */
    UINT16 len;  /* the host length of URL, or entire length if keyword */
    UINT16 attr; /* path or keyword type */
    } FW_URL_NODE;

/* URL database descriptor */

typedef struct {
    int type;     /* type: URL filter type */ 
    SEM_ID semId; /* semaphore to protect the URL database operation */
    DL_LIST list; /* URL database */
    } URL_BASE_DESC;

/* Local variables */
    
LOCAL int       urlAbsUri = URL_CLASS_ABS_URI;

/* local functions */

LOCAL UINT32 cksumAdjust (UINT32, UINT16 *, int, BOOL);
LOCAL char * httpPktLocate (M_BLK_ID, char **, int *, UINT16 **);
LOCAL int strStLen (char *, char, int);
LOCAL BOOL strCiCmp (char *, char *, int, char, char);
LOCAL BOOL isCiStringSame (char *, char *);
LOCAL UINT32 objFilter (M_BLK_ID, void *);
LOCAL BOOL fieldNameFind (char *, char *, int *);

/*******************************************************************************
*
* fwUrlFilter - perform the URL filtering for the HTTP packets
*
* This routine filters the HTTP request packets based on the given URL
* database. It performs two match actions: the path match and keyword match.
* The path match means both the host name and the file path of the provided URL
* string matches the packet's ones. However, the given file path just needs to
* match the beginning portion of the packet's path. The keyword match means the
* given string matches any part of the packet's URL.
*
* There is no semaphore protection for the URL database when the fwUrlFilter 
* searches through the list. User should ensure the deletion of URL entry
* should occur either in the tasks have lower priority than tNetTask or 
* tNetTask or before the URL filter is installed. The assumption is the 
* deletion generally will not happen too often and the semaphore protection
* operation is costly in the performance critical path. 
*
* RETURNS: FW_SVC_MATCH_DONE or FW_SVC_NOT_MATCH.
*
* ERRNO: N/A
*/

UINT32 fwUrlFilter 
    (
    M_BLK_ID pMblk,   /* the IP packet containing the HTTP packet */
    void * usrCtxt    /* URL database, supplied at the time of registering  */
    )
    {
    int cnt;
    char * pHttp;
    char * pHost;
    char * pPath;
    int    httpLen;
    int    pathLen;
    int    hostLen;
    URL_BASE_DESC * pDesc;
    char * pPktBuf = NULL; /* packet buffer if packet is not in a single mblk */
    FW_URL_NODE * pNode;
    UINT32 retCode = FW_SVC_NOT_MATCH;
 
    pDesc = (URL_BASE_DESC *)usrCtxt; 

    /* proceed to the beginning of the HTTP packet */

    if ((pHttp = httpPktLocate(pMblk,&pPktBuf,&httpLen,NULL)) == NULL)
        goto doneExit;

    DBG_PRINT(("the first 8 chars of HTTP packet: %c%c%c%c%c%c%c%c\n",
               pHttp[0],pHttp[1],pHttp[2],pHttp[3],
               pHttp[4],pHttp[5],pHttp[6],pHttp[7]));

    /* skip the method token, advance to URI */

    cnt = strStLen(pHttp,' ',httpLen);
    pHttp += cnt+1;

    /* Request-URI = "*" | absoluteURI | abs_path | authority
     * "*" and authority forms are used by OPTION and CONNECT methods
     * absoluteURI   = scheme ":" ( hier_part | opaque_part )
     * hier_part     = ( net_path | abs_path ) [ "?" query ]
     * net_path      = "//" authority [ abs_path ]
     * abs_path      = "/"  path_segments    
     *
     * Host name case insensitive, scheme case insensitive
     */

    /* consider absoluteURI and abs_path */
 
    if (strCiCmp(pHttp,TOKEN_HTTP,TMNATOR_TYPE_SPECIFIC,':',0) == TRUE)
        {
        /* absoluteURI 
         * HTTP/1.1 clients only generates absoluteURI in requests to
         * proxies(RFC2616, 5.1.2).
         */
        if (pDesc->type == URL_CLASS_ABS_URI)
            {
            retCode = FW_SVC_MATCH_DONE;
            goto doneExit;
            }

        pHttp += HTTP_S2H_LEN;
        pHost = pHttp;
        hostLen = strStLen(pHost,'/',httpLen);
        pPath = pHttp + hostLen;
        pathLen = strStLen(pPath,' ',httpLen);  /* terminated by space */
        }
    else
        {
        /* not absolute URI */

        if (pDesc->type == URL_CLASS_ABS_URI)
            {
            retCode = FW_SVC_NOT_MATCH;
            goto doneExit;
            }

        /* abs_path */
        pPath = pHttp;
        pathLen = strStLen(pPath,' ',httpLen);   

        if (fieldNameFind(pHttp,TOKEN_HOST,&cnt) == FALSE)
            goto doneExit;        

        pHttp += cnt;
        pHttp += NEXT_FIELD_POS(TOKEN_HOST);
        SKIP_SPACES(pHttp);
        pHost = pHttp;
        hostLen = strStLen(pHost,'\r',0);
        }

    for (pNode = (FW_URL_NODE *)DLL_FIRST(&pDesc->list); pNode; 
         pNode=(FW_URL_NODE *)DLL_NEXT(&pNode->node) )
        {
        if (pNode->attr == FW_URL_SPECIFIC_PATH)
            {
            DBG_PRINT(("usrUrlHost: %s, httpHost: %s\n",pNode->pUrl,pHost));
            if (strCiCmp(pNode->pUrl,pHost,TMNATOR_TYPE_GENERIC,0,0))
                {
                DBG_PRINT(("Host match\n"));
                if ((pNode->pUrl[pNode->len] == 0) || IS_STRING_SAME(pPath,
                        pNode->pUrl + pNode->len))
                    break;
                }
            }
        else  /* FW_URL_KEYWORD */
            {
            if (fastStrSearch(pNode->pUrl,pNode->len,pHost,hostLen,FALSE) ||
                fastStrSearch(pNode->pUrl,pNode->len,pPath,pathLen,TRUE) )
                break;
            }
        }    
   
    /* check if matches */

    if (pNode != NULL)
        retCode = FW_SVC_MATCH_DONE;  /* found the match */
    
doneExit:
    if (pPktBuf != NULL)
        free(pPktBuf);
    return retCode;
    }   

/*******************************************************************************
*
* fwProxyFilter - Filter the HTTP proxy packets
*
* This routine blocks the HTTP proxy packets sent on the same destination port
* the HTTP content filter attached to.
*
* RETURNS: FW_SVC_MATCH_DONE or FW_SVC_NOT_MATCH.
*
* ERRNO: N/A
*/
UINT32 fwProxyFilter
    (
    M_BLK_ID pMblk,   /* the IP packet containing the HTTP packet */
    void * usrCtxt    /* Not used */
    )
    {
    return fwUrlFilter(pMblk,(void *)&urlAbsUri);
    }

/*******************************************************************************
*
* fwCookieFilter - Prevent the cookie from being sent back to the web server.
*
* This routine prevents the cookie from being sent back to the web servers by 
* overwritting the cookie in the client's request message header.
*
* RETURNS: FW_SVC_MATCH_NEXT or FW_SVC_NOT_MATCH.
*
* ERRNO: N/A
*/
                                                                                
UINT32 fwCookieFilter
    (
    M_BLK_ID pMblk,   /* the IP packet containing the HTTP packet */
    void * usrCtxt    /* Not used */
    )
    {
    char * pHttp;
    UINT16 * pOrgCksum;
    UINT16 * pCksumStart;
    char * pPktBuf = NULL;
    int httpLen;
    int offset;
    int cookieLen;
    int csLen;
    int ckTokenLen;
    UINT32 newCksum = 0;

    if ((pHttp = httpPktLocate(pMblk,&pPktBuf,&httpLen,&pOrgCksum)) != NULL)
        {
        if (fieldNameFind(pHttp,"Cookie",&offset) == TRUE)
            {
            pHttp += offset;

            cookieLen = strStLen(pHttp,'\r',0);
            if (!ALIGNED(pHttp,2))
                {
                pCksumStart = (UINT16 *)(pHttp - 1);
                csLen = (cookieLen & 0x1)? cookieLen + 1: cookieLen + 2;
                }
            else
                {
                pCksumStart = (UINT16 *)pHttp;
                csLen = (cookieLen & 0x1)?  cookieLen + 1: cookieLen;
                }

            /* if this flag is set, indicate the checksum is not calculated
             * yet. It will be calculated in the ip_output(). So no need to
             * worry about the checksum. This flag should only apply to the
             * packet generated on the local host
             */ 
            if (!(pMblk->m_pkthdr.csum_flags & CSUM_DELAY_DATA))
                {
                /* subtract the cookie checksum data from TCP checksum */

                newCksum = cksumAdjust(*pOrgCksum,pCksumStart,csLen,FALSE);
                }

            /* remove the Cookie information by filling some junk data */

            ckTokenLen = strStLen(pHttp,':',0);       
            bfill(pHttp,ckTokenLen,'d');

            /* not to overwrite the : and space */

            bfill(pHttp + ckTokenLen + 2 ,cookieLen - (ckTokenLen + 2),'x');

            if (!(pMblk->m_pkthdr.csum_flags & CSUM_DELAY_DATA))
                {
                /*add the new Cookie data to the adjusted TCP checksum */
        
                newCksum = cksumAdjust(newCksum,pCksumStart,csLen,TRUE);
                *pOrgCksum = newCksum; 

                if (pPktBuf != NULL)
                    {
                    /* copy back the new checksum */    

                    netMblkFromBufCopy(pMblk,(char *)pOrgCksum,
                                   (char *)pOrgCksum - pPktBuf,
                                   2,
                                   M_DONTWAIT,
                                   NULL);
                    }
                }

            if (pPktBuf != NULL)
                {
                /* message in multiple mbufs, operate in a temp buf, copyback
                 * junk cookie data
                 */

                netMblkFromBufCopy(pMblk,pHttp,
                                   pMblk->m_pkthdr.len - httpLen + offset,
                                   cookieLen,
                                   M_DONTWAIT,
                                   NULL);


                                         
                free(pPktBuf);
                }
              
            return FW_SVC_MATCH_NEXT;
            }
        }

    if (pPktBuf != NULL)
        free(pPktBuf);
    return FW_SVC_NOT_MATCH;
    }

/*******************************************************************************
*
* fwJavaAppletFilter - Filter Java Applet object
*
* This routine blocks the HTTP requests to the files with extension of class,
* jar and zip.  
*
* RETURNS: FW_SVC_MATCH_DONE or FW_SVC_NOT_MATCH.
*
* ERRNO: N/A
*/

UINT32 fwJavaAppletFilter 
    (
    M_BLK_ID pMblk,   /* the IP packet containing the HTTP packet */
    void * usrCtxt    /* Not used */
    )
    {
    return objFilter(pMblk,(void *)OBJ_FILTER_JAVA);
    }

/*******************************************************************************
*
* fwActiveXFilter - Filter ActiveX control object
*
* This routine blocks the HTTP requests to the files with extension of ocx and
* cab.
*
* RETURNS: FW_SVC_MATCH_DONE or FW_SVC_NOT_MATCH.
*
* ERRNO: N/A
*/
                                                                                   
UINT32 fwActiveXFilter
    (
    M_BLK_ID pMblk,   /* the IP packet containing the HTTP packet */
    void * usrCtxt    /* Not used */
    )
    {
    return objFilter(pMblk,(void *)OBJ_FILTER_ACTIVEX);
    }

/*******************************************************************************
*
* objFilter - Block the executable binary object
*
* This routine prevents certain executable files from running in the web 
* browser by blocking the requests to the corresponding type of files. Currently
* it blocks the Java Applet by blocking the files with extension class, jar 
* and zip; and ActiveX control by blocking the files with extension ocx and 
* cab.
*
* RETURNS: FW_SVC_MATCH_DONE or FW_SVC_NOT_MATCH.
*
* ERRNO: N/A
*
* NOMANUAL
*/

LOCAL UINT32 objFilter 
    (
    M_BLK_ID pMblk,   /* the IP packet containing the HTTP packet */
    void * usrCtxt    /* objFilter type,  supplied at the time of registering */
    )
    {
    char * pHttp;
    char * pPktBuf=NULL;
    int httpLen;
    int cnt;
   
    UINT32 retCode = FW_SVC_NOT_MATCH;

    if ((pHttp = httpPktLocate(pMblk,&pPktBuf,&httpLen,NULL)) != NULL)
        {
        if (IS_STRING_SAME(pHttp,TOKEN_GET))
            {
            /* advance pass GET token and space */

            pHttp += NEXT_FIELD_POS(TOKEN_GET);
            cnt = strStLen(pHttp,' ',httpLen);
            pHttp += cnt;

            /* search for file extension seperator */

            for (cnt=0; (cnt < FILE_EXT_MAXLEN) && (*pHttp-- != '.'); cnt++);

            if (cnt < FILE_EXT_MAXLEN)
                {
                pHttp += 2;

                if ((UINT32)usrCtxt == OBJ_FILTER_ACTIVEX)
                    {
                    if (isCiStringSame(pHttp,"ocx") || 
                        isCiStringSame(pHttp,"cab") )
                        retCode = FW_SVC_MATCH_DONE;
                    }
                else if ((UINT32)usrCtxt == OBJ_FILTER_JAVA) /* Java Applet */
                    { 
                    if (isCiStringSame(pHttp,"class") ||
                        isCiStringSame(pHttp,"jar")   ||
                        isCiStringSame(pHttp,"zip") )
                        retCode = FW_SVC_MATCH_DONE;
                    }
                }
            }
        }

    if (pPktBuf != NULL)
        free(pPktBuf);

    return retCode;
    }
    
/*******************************************************************************
*
* isCiStringSame - Compare two case insensitive strings     
*
* This routine compares two case insensitive strings.
*
* RETURNS: TRUE or FALSE
*
* ERRNO: N/A
*
* NOMANUAL
*/
LOCAL BOOL isCiStringSame
    (
    char * pStr,
    char * pIdStr
    )
    {
    int len;
    int i;

    len = strlen(pIdStr);
    for (i = 0; i < len; i++)
        if (tolower((UINT8)pStr[i]) != tolower((UINT8)pIdStr[i]))
            return FALSE;
    return TRUE;
    }
  
/*******************************************************************************
*
* strCiCmp - Compare two case insensitive strings with specific terminators 
*
* This routine compares two case insensitive strings with specific terminators.
*
* RETURNS: TRUE or FALSE
*
* ERRNO: N/A
*
* NOMANUAL
*/
LOCAL BOOL strCiCmp 
    (
    char * pStr1,
    char * pStr2,
    int     typeMatch,
    char   term1,
    char   term2
    )
    {
    int cnt = 0;
    BOOL cond1;
    BOOL cond2;

    for(;;)
        {
        if (TMNATOR_TYPE(typeMatch) == TMNATOR_TYPE_SPECIFIC)
            {
            cond1 = (pStr1[cnt] == term1);
            cond2 = (pStr2[cnt] == term2);
            }
        else  /* TMNATOR_TYPE_GENERIC */
            {
            cond1 = STR_GENERIC_TMNATOR(pStr1[cnt]);
            cond2 = STR_GENERIC_TMNATOR(pStr2[cnt]);
            }

        if (cond1 && cond2)
            return TRUE;
        else if (cond1 || cond2)
            {
            if (TMNATOR_MATCH(typeMatch) == LOOSE_MATCH)
                return TRUE;
            else
                return FALSE;
            }
          
        if (tolower((UINT8)pStr1[cnt]) != tolower((UINT8)pStr2[cnt]))
            return FALSE;
        cnt++;
        }

    /* should never reach here */

    return FALSE;
    }

/*******************************************************************************
*
* strStLen - the length of string with specific terminator
*
* This routine returns the length of string with specific terminator
*
* RETURNS: length of string
*
* ERRNO: N/A
*
* NOMANUAL
*/

LOCAL int strStLen
    (
    char * pStr,        /* string */
    char  terminator,
    int maxLen
    )
    {
    int i;

    if (maxLen == 0)
        maxLen = INSPECTED_SIZE_MAX;

    for (i = 0; i < maxLen; i++)
        { 
        if (pStr[i] == terminator)
            return i;
        }
    return 0;
    }

/******************************************************************************
*
* httpPktLocate - Locate at the start of HTTP packet
*
* This routine locates the start of HTTP packet
*
* RETURNS: pointer to the start of HTTP packet.
*
* ERRNO: N/A
*
* NOMANUAL
*/

LOCAL char * httpPktLocate
    (
    M_BLK_ID pMblk,
    char ** ppPktBuf,
    int * pHttpLen,
    UINT16 ** ppCksum
    )
    {
    char * pTemp = NULL;
    int len;
    struct ip * pIp = NULL;
#ifdef INET6
    struct ip6_hdr * pIpv6 = NULL;
#endif
   char * pHttp;
   int    offset;

    *ppPktBuf = NULL;

    /* make sure it is in a contiguous memory when performing the parsing */
    if (pMblk->m_len < pMblk->m_pkthdr.len)
        {
        DBG_PRINT(("FW Ctent: data in multiple mbuf: mbuf1: %d, plen: %d\n",
                   pMblk->m_len,pMblk->m_pkthdr.len));
                                                                                
        /* packet in multiple mbufs, copy to a temp buf */
                                                                                
        len = min(pMblk->m_pkthdr.len,INSPECTED_SIZE_MAX);
                                                                                
        if ((pTemp = (char *)malloc(len)) == NULL)
            {
            DBG_PRINT(("FW Ctent: Error, malloc fails\n"));
            return NULL;
            }
                                                                                
        if (!netMblkOffsetToBufCopy(pMblk,0,pTemp,len,NULL))
            {
            DBG_PRINT(("FW Ctent: Error, mbuf copy fails\n"));
            free(pTemp);
            return NULL;  /* no payload, just return */
            }
        *ppPktBuf = pTemp;
        }
                                                                                
    if ((*mtod(pMblk,UINT8 *) & IPV6_VERSION_MASK) == IPV4_VERSION)
        {
        pIp = (pTemp == NULL)? mtod(pMblk,struct ip *): (struct ip *)pTemp;
                                                                                
        offset = (pIp->ip_hl << 2);
        pHttp = (char *)pIp + offset;
        }
#ifdef INET6
    else if ((*mtod(pMblk,UINT8 *) & IPV6_VERSION_MASK) == IPV6_VERSION)
        {
        int proto;
                                                                                
        pIpv6 = (pTemp == NULL)? mtod(pMblk,struct ip6_hdr *):
                (struct ip6_hdr *)pTemp;
                                                                                
        ipv6UpHdrOffsetGet(pIpv6,&proto,&offset,
                          pMblk->m_pkthdr.len  - sizeof(struct ip6_hdr));
        pHttp = (char *)pIpv6 + offset;
        }
#endif
    else  /* don't know about this type of packet */
        {
        if (pTemp)
            {
            free(pTemp);
            *ppPktBuf = NULL;
            }
        return NULL;
        }
    if (ppCksum != NULL)
        *ppCksum = &((struct tcphdr *)pHttp)->th_sum;

    offset += (((struct tcphdr *)pHttp)->th_off << 2);
    pHttp += (((struct tcphdr *)pHttp)->th_off << 2);
                                                                                
    if (pMblk->m_pkthdr.len <= offset)
        {
        DBG_PRINT(("pMlk=0x%x, pktLen=%d,offset = %d\n",
               (int)pMblk,pMblk->m_pkthdr.len,offset));
                                                                                
        /* no payload in the packet, just return */

        if (pTemp)
            {
            free(pTemp);
            *ppPktBuf = NULL;
            }
        return NULL;
        }
    *pHttpLen = pMblk->m_pkthdr.len - offset;
    return pHttp;
    }
                                                                                

/****************************************************************************
*
* cksumAdjust - Adjust the TCP checksum for modified packet
*
* Assume data starts at even boundary with even length.
*
* RETURNS: the new checksum
*
* ERRNO: N/A
*
* NOMANUAL
*/  
LOCAL UINT32 cksumAdjust 
    (
    UINT32 cksum, 
    UINT16 * pData,
    int dataLen,
    BOOL isAddingData  /* either adding or deleting data */
    )
    {
    cksum = (~cksum & 0x0000FFFF);

    if (isAddingData == FALSE) 
        {  
        /* removing data */
                                  
        while (dataLen > 0x0000)
            {
            cksum = cksum - ((*pData++) & 0x0000FFFF);
                                                                                
            if ((INT32) cksum <= 0x00000000L)
                {
                --cksum;
                cksum = cksum & 0x0000FFFF;
                }
                                                                                
            dataLen -= 2;
            }
        }
    else   /* adding data */  
        {                                                                         
        while (dataLen > 0x0000)
            {
            cksum = cksum + ((*pData++) & 0x0000FFFF);
                                                                                
            if (cksum & 0x00010000)
                {
                ++cksum;
                cksum = cksum & 0x0000FFFF;
                }
            dataLen -= 2;
            }
        }                                                                                
    cksum = ~cksum;
    return cksum;
    }

/********************************************************************************
* fieldNameFind - Find a string in a structured message
*
* This routine searches a string in a sturctured message. It assumes the
* searched string always starts at the begining of each line of data
* terminated by \r\n characters. A message should be terminated by \r\n
* preceded by the line terminator \r\n.
*
* RETURNS: TRUE found or FALSE not found
*
* ERRNO: N/A
*
* NOMANUAL
*/
                                                                                
LOCAL BOOL fieldNameFind
    (
    char * pData,   /* does not terminated by 0 */
    char * pIdStr,  /* ID string terminated by 0 */
    int  * pPos     /* found string position relative to pData */
    )
    {
    int searchLen;
    int i = 0;
    char * pStart = pData;

    if (pIdStr == NULL)
        return FALSE;
                                                                                
    searchLen = INSPECTED_SIZE_MAX;  /* approxi. max search length */
                                                                                
    while (TRUE)
        {
        /* skip all space */
                                                                                
        while (*pData == ' ')
            pData++;
                                                                                
        if (IS_STRING_SAME(pData, pIdStr))
            break;
                                                                                
        /* advance to next line */
                                                                                
        for (i = 0; i < (searchLen - 1); i++)
            {
            if (pData[i] == '\r')
                if (pData[i+1] == '\n')
                    break;
            }
                                                                                
        if (i >= (searchLen - 1))
            {
            DBG_PRINT(("NAU:single message exceeds max len: %d\n",searchLen));
            return FALSE;
            }
                                                                                
        pData += i + 2;
        searchLen -= (i + 2);
                                                                                
        if ((pData[0] == '\r' && pData[1] == '\n') || (searchLen <= 0))
            {
            /* reach the end mark \r\n\r\n */

            return FALSE;
            }
        }
                                                                                
    *pPos = pData - pStart;
    return TRUE;
    }

/********************************************************************************
* fwUrlAdd - Add a URL string to the URL database
*
* The routine adds a URL string to a given database for the URL filter.
* There are two types of string, path and keyword. The path type indicates
* the string is a URL path. The string should at least have a host name portion.* The keyword type indicates the string is a word in a URL.
*
* RETURNS: the ID of this URL in the database or NULL if fails.
*
* ERRNO: N/A
*/
                                                                                
void * fwUrlAdd
    (
    void * pDesc,   /* the given URL database */
    char * pUrl,    /* the added URL string */
    int    type     /* the type of URL string, path or keyword */
    )
    {
    FW_URL_NODE * pNode;
    DL_LIST * pList;
                                                                                
    if (pDesc == NULL || pUrl == NULL)
        return NULL;
                                                                                
    pList = &((URL_BASE_DESC *)pDesc)->list;
                                                                                
    if ((pNode = (FW_URL_NODE *)malloc(sizeof(FW_URL_NODE) +
          strlen(pUrl) + 1)) == NULL)
        {
        DBG_PRINT(("ERROR: malloc failed\n"));
        return NULL;
        }
    pNode->pUrl = (char *)(pNode + 1);
    if (strCiCmp(pUrl,TOKEN_HTTP,TMNATOR_TYPE_SPECIFIC,':',0))
        if (IS_STRING_SAME((pUrl + strlen(TOKEN_HTTP)),"://"))
            pUrl += HTTP_S2H_LEN;
                                                                                
    pNode->attr = type;
    strcpy(pNode->pUrl,pUrl);
    if (pNode->attr == FW_URL_SPECIFIC_PATH)
        {
        pNode->len = strStLen(pNode->pUrl,'/',strlen(pNode->pUrl));
        if (!pNode->len)
            pNode->len = strlen(pNode->pUrl);
        }
    else
        pNode->len = strlen(pNode->pUrl);
                                                                                
    semTake(((URL_BASE_DESC *)pDesc)->semId,WAIT_FOREVER);
    dllAdd(pList,&pNode->node);
    semGive(((URL_BASE_DESC *)pDesc)->semId);
    return (void *)pNode;
    }

/******************************************************************************
*
* urlPrint - Print the URL node
*
* This routine prints the URL node for debugging and diagnostic purposes.
*
* RETURNS: TRUE always
*
* ERRNO: N/A
*
* NOMANUAL
*/

LOCAL int urlPrint 
    (
    DL_NODE * pNode,
    int arg
    )
    {
    FW_URL_NODE * pUrlNode = (FW_URL_NODE *)pNode;

    printf("Node: 0x%x, URL: %s, %s\n",
           (int)pNode,
           pUrlNode->attr==FW_URL_SPECIFIC_PATH? "path":"word",
           pUrlNode->pUrl);
    return TRUE;
    }

/*******************************************************************************
*
* fwUrlListCreate - Create a empty URL list database 
*
* This routine returns the created URL database ID.
*
* RETURNS: the database ID or NULL if fails
*
* ERRNO: N/A
*/

void * fwUrlListCreate (void)
    {
    URL_BASE_DESC * pDesc;

    if ((pDesc = (URL_BASE_DESC *)malloc(sizeof(URL_BASE_DESC))) == NULL)
        return NULL;

    if ((pDesc->semId = semBCreate(SEM_Q_PRIORITY,SEM_FULL)) == NULL)
        {
        free(pDesc);
        return NULL;
        }

    pDesc->type = URL_CLASS_INDIVIDUAL;
    dllInit(&pDesc->list);
    return (void *)pDesc;
    }

/*******************************************************************************
*
* fwUrlListDelete - Delete a given URL database
* 
* This routine frees all the memory resource associated with the given
* URL database.
*
* RETURNS: TRUE or FALSE if not succesful
*
* ERRNO: N/A
*/
BOOL fwUrlListDelete
    (
    void * pDesc
    )
    {
    DL_NODE * pNode;

    if (pDesc == NULL)
        return FALSE;

    semTake(((URL_BASE_DESC *)pDesc)->semId,WAIT_FOREVER);       
    while ((pNode = dllGet(&((URL_BASE_DESC *)pDesc)->list)))
        free(pNode);
    semGive(((URL_BASE_DESC *)pDesc)->semId);     
    semDelete(((URL_BASE_DESC *)pDesc)->semId);  
    free(pDesc);
    return TRUE;
    }

/***************************************************************************
*
* fwUrlDelete - Delete a given URL entry from a given database
*
* This routine free the memory resource associated with the given URL node.
* 
* RETURNS : TRUE or FALSE if not successful
*
* ERRNO: N/A
*/
BOOL fwUrlDelete 
    (
    void * pDesc,
    void * pNode
    )
    {
    if (pDesc == NULL || pNode == NULL)
        return FALSE;

    semTake(((URL_BASE_DESC *)pDesc)->semId,WAIT_FOREVER);       
    dllRemove(&((URL_BASE_DESC *)pDesc)->list,(DL_NODE *)pNode);
    semGive(((URL_BASE_DESC *)pDesc)->semId);       
    free(pNode); 
    return TRUE;
    } 

/*****************************************************************************
*
* fwUrlMoveAfter - Move a given URL entry after to a specified entry.
*
* This routine moves a given URL node after to a specified URL node.
*
* RETURNS: TRUE or FALSE
*
* ERRNO: N/A
*/

BOOL fwUrlMoveAfter
    ( 
    void * pDesc,
    void * pPrev,
    void * pNode
    )
    {
    if (pDesc == NULL || pNode == NULL)
        return FALSE;

    semTake(((URL_BASE_DESC *)pDesc)->semId,WAIT_FOREVER);       
    dllRemove(&((URL_BASE_DESC *)pDesc)->list,(DL_NODE *)pNode); 
    dllInsert(&((URL_BASE_DESC *)pDesc)->list,(DL_NODE *)pPrev,
              (DL_NODE *)pNode);
    semGive(((URL_BASE_DESC *)pDesc)->semId);     
    return TRUE;  
    }

/******************************************************************************
*
* fwUrlShow - Display the given URL database
*
* This routine displas the given URL database for debugging and diagnostic
* purposes.
*
* RETURNS: N/A
*
* ERRNO: N/A
*
* NOMANUAL
*/
void fwUrlShow 
    (
    void * pDesc
    )
    {
    if (pDesc == NULL)
        return;

    semTake(((URL_BASE_DESC *)pDesc)->semId,WAIT_FOREVER);       
    dllEach(&((URL_BASE_DESC *)pDesc)->list,(FUNCPTR)urlPrint,0);
    semGive(((URL_BASE_DESC *)pDesc)->semId);       
    }