#include "precomp.h"
#pragma hdrstop


TOKEN_VALUE InterfaceTypes[ 5 ] =
{
    { VAL_CLIENT,       ROUTER_IF_TYPE_CLIENT },
    { VAL_HOMEROUTER,   ROUTER_IF_TYPE_HOME_ROUTER },
    { VAL_WANROUTER,    ROUTER_IF_TYPE_FULL_ROUTER },
    { VAL_DEDICATED,    ROUTER_IF_TYPE_DEDICATED },
    { VAL_INTERNAL,     ROUTER_IF_TYPE_INTERNAL }
};

TOKEN_VALUE InterfaceStates[ 3 ] = 
{
    { VAL_DOWN,         ROUTER_IF_STATE_DISCONNECTED },
    { VAL_DOWN,         ROUTER_IF_STATE_CONNECTING },
    { VAL_UP,           ROUTER_IF_STATE_CONNECTED }
};

TOKEN_VALUE InterfaceEnableStatus[ 2 ] =
{
    { VAL_ENABLED,      FALSE },
    { VAL_DISABLED,     TRUE }
};


TOKEN_VALUE AdminStates[ 2 ] = 
{
    { VAL_DISABLED,     ADMIN_STATE_DISABLED },
    { VAL_ENABLED,      ADMIN_STATE_ENABLED }
};

TOKEN_VALUE OperStates[ 3 ] =
{
    { VAL_DOWN,         OPER_STATE_DOWN },
    { VAL_UP,           OPER_STATE_UP     },
    { VAL_SLEEPING,     OPER_STATE_SLEEPING }
};

TOKEN_VALUE IpxInterfaceTypes[ 8 ] = 
{
    { VAL_OTHER,        IF_TYPE_OTHER },
    { VAL_DEDICATED,    IF_TYPE_LAN },
    { VAL_WANROUTER,    IF_TYPE_WAN_ROUTER },
    { VAL_CLIENT,       IF_TYPE_WAN_WORKSTATION },
    { VAL_INTERNAL,     IF_TYPE_INTERNAL },
    { VAL_HOMEROUTER,   IF_TYPE_PERSONAL_WAN_ROUTER },
    { VAL_DIALOUT,      IF_TYPE_ROUTER_WORKSTATION_DIALOUT },
    { VAL_DIALOUT,      IF_TYPE_STANDALONE_WORKSTATION_DIALOUT }
};

TOKEN_VALUE RouterInterfaceTypes[ 5 ] =
{
    { VAL_CLIENT,       ROUTER_IF_TYPE_CLIENT },
    { VAL_HOMEROUTER,   ROUTER_IF_TYPE_HOME_ROUTER },
    { VAL_WANROUTER,    ROUTER_IF_TYPE_FULL_ROUTER },
    { VAL_DEDICATED,    ROUTER_IF_TYPE_DEDICATED },
    { VAL_INTERNAL,     ROUTER_IF_TYPE_INTERNAL }
};

TOKEN_VALUE NbDeliverStates[ 4 ] =
{
    { VAL_DISABLED,     ADMIN_STATE_DISABLED },
    { VAL_ENABLED,      ADMIN_STATE_ENABLED },
    { VAL_STATICONLY,   ADMIN_STATE_ENABLED_ONLY_FOR_NETBIOS_STATIC_ROUTING },
    { VAL_ONLYWHENUP,   ADMIN_STATE_ENABLED_ONLY_FOR_OPER_STATE_UP}
};


TOKEN_VALUE UpdateModes[ 3 ] =
{
    { VAL_STANDARD,     IPX_STANDARD_UPDATE },
    { VAL_NONE,         IPX_NO_UPDATE },
    { VAL_AUTOSTATIC,   IPX_AUTO_STATIC_UPDATE }
};

TOKEN_VALUE IpxProtocols[ 4 ] =
{
    { VAL_LOCAL,        IPX_PROTOCOL_LOCAL },
    { VAL_STATIC,       IPX_PROTOCOL_STATIC },
    { VAL_RIP,          IPX_PROTOCOL_RIP },
    { VAL_SAP,          IPX_PROTOCOL_SAP }
};  

TOKEN_VALUE TfFilterActions[ 2 ] = 
{
    { VAL_PERMIT,       IPX_TRAFFIC_FILTER_ACTION_PERMIT },
    { VAL_DENY,         IPX_TRAFFIC_FILTER_ACTION_DENY }
};

TOKEN_VALUE RipFilterActions[ 2 ] = 
{
    { VAL_PERMIT,       IPX_ROUTE_FILTER_PERMIT },
    { VAL_DENY,         IPX_ROUTE_FILTER_DENY }
};


TOKEN_VALUE SapFilterActions[ 2 ] =
{
    { VAL_PERMIT,       IPX_SERVICE_FILTER_PERMIT },
    { VAL_DENY,         IPX_SERVICE_FILTER_DENY }
};


TOKEN_VALUE FilterModes[ 2 ] = 
{
    { VAL_INPUT,        INPUT_FILTER },
    { VAL_OUTPUT,       OUTPUT_FILTER }
};

TOKEN_VALUE LogLevels[ 4 ] = 
{
    { VAL_NONE ,        0 },
    { VAL_ERRORS_ONLY , EVENTLOG_ERROR_TYPE },
    { VAL_ERRORS_AND_WARNINGS,  EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE },
    { VAL_MAXINFO, 
        EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE }
};


DWORD
GetIpxInterfaceIndex(
    IN      MIB_SERVER_HANDLE   hRouterMIB,
    IN      LPCWSTR             InterfaceName,
    OUT     ULONG              *InterfaceIndex
    )
/*++

Routine Description :

    This routine retrives the index of an interface given its name.

Arguments :

    hRouterMIB - Handle to the router service

    InterfaceName - Name of interface for which index is required

    InterfaceIndex - On return contains the interface of the interface
                     if found.

Return values :

    
--*/
{

    IPX_MIB_GET_INPUT_DATA  MibGetInputData;
    DWORD                   IfSize = sizeof(IPX_INTERFACE);
    PIPX_INTERFACE          Ifp;
    DWORD                   rc;
    UCHAR                   InterfaceNameA[ MAX_INTERFACE_NAME_LEN + 1 ];

    
    //
    // Convert interface name to Ansi
    //

    wcstombs( InterfaceNameA, InterfaceName, MAX_INTERFACE_NAME_LEN );

    MibGetInputData.TableId = IPX_INTERFACE_TABLE;

    //
    // Begin enumerating interfaces
    //
    
    rc = MprAdminMIBEntryGetFirst(
            hRouterMIB, PID_IPX, IPX_PROTOCOL_BASE, &MibGetInputData,
            sizeof( IPX_MIB_GET_INPUT_DATA ), (LPVOID *) &Ifp,
            &IfSize
            );

    //
    // until a match is found or there are no more interfaces
    //
    
    while ( rc == NO_ERROR )
    {
        //
        // Is this the interface
        //
        
        if ( _stricmp( (LPSTR)InterfaceNameA, (LPSTR) Ifp->InterfaceName) == 0 )
        {
            *InterfaceIndex = Ifp->InterfaceIndex;
            
            MprAdminMIBBufferFree (Ifp);
            
            break;
        }
        else 
        {
            MibGetInputData.MibIndex.InterfaceTableIndex.InterfaceIndex = 
                Ifp->InterfaceIndex;
                
            MprAdminMIBBufferFree (Ifp);
        }
        
        rc = MprAdminMIBEntryGetNext(
                hRouterMIB, PID_IPX, IPX_PROTOCOL_BASE, &MibGetInputData,
                sizeof(IPX_MIB_GET_INPUT_DATA), (LPVOID *)&Ifp, &IfSize
                );
    }

    if ( rc == ERROR_NO_MORE_ITEMS )
    {
        rc = ERROR_NO_SUCH_INTERFACE;
    }
    
    return rc;
}

DWORD
GetIpxInterfaceName (
    IN      MIB_SERVER_HANDLE   hRouterMIB,
    IN      ULONG               InterfaceIndex,
    OUT     LPWSTR              InterfaceName
    ) 
/*++

Routine Description :

    This routine retrives the index of an interface given its name.

Arguments :

    hRouterMIB - Handle to the router service

    InterfaceName - Name of interface for which index is required

    InterfaceIndex - On return contains the interface of the interface
                     if found.

Return values :

    
--*/
{
    IPX_MIB_GET_INPUT_DATA  MibGetInputData;
    DWORD                   IfSize = sizeof( IPX_INTERFACE );
    PIPX_INTERFACE          Ifp;
    DWORD                   rc;


    MibGetInputData.TableId = IPX_INTERFACE_TABLE;
    
    MibGetInputData.MibIndex.InterfaceTableIndex.InterfaceIndex = 
        InterfaceIndex;
    
    rc = MprAdminMIBEntryGet(
            hRouterMIB, PID_IPX, IPX_PROTOCOL_BASE, &MibGetInputData,
            sizeof(IPX_MIB_GET_INPUT_DATA), (LPVOID *)&Ifp, &IfSize
            );
            
    if ( rc == NO_ERROR )
    {
        mbstowcs( 
            InterfaceName, (LPSTR)Ifp->InterfaceName,
            IPX_INTERFACE_ANSI_NAME_LEN
            );

        MprAdminMIBBufferFree( Ifp );
    }
    
    else if ( rc == ERROR_NO_MORE_ITEMS )
    {
        rc = ERROR_NO_SUCH_INTERFACE;
    }
    
    return rc;
}



/*++
*******************************************************************
        G e t I P X T o c E n t r y

Routine Description:
    Returns pointer to entry in Router Table Of Context
Arguments:
    pInterfaceInfo    - pointer to table of content
    InfoEntryType    - type of entry to look for
Return Value:
    Pointer to entry in table of content
    NULL if there is no such entry in the table
Remarks:
*******************************************************************
--*/
PIPX_TOC_ENTRY
GetIPXTocEntry(
    IN PIPX_INFO_BLOCK_HEADER   pInterfaceInfo,
    IN ULONG                    InfoEntryType
    ) 
{
    UINT            i;
    PIPX_TOC_ENTRY  pTocEntry;

    if (pInterfaceInfo)
    {
        for ( i = 0, pTocEntry = pInterfaceInfo->TocEntry;
              i < pInterfaceInfo->TocEntriesCount;
              i++, pTocEntry++) 
        {
            if (pTocEntry->InfoType == InfoEntryType) 
            {
                return pTocEntry;
            }
        }
    }        

    SetLastError( ERROR_FILE_NOT_FOUND );
    
    return NULL;
}


DWORD
AddIPXInfoEntry (
    IN PIPX_INFO_BLOCK_HEADER       pOldBlock,
    IN ULONG                        InfoType,
    IN ULONG                        InfoSize,
    IN PVOID                        Info,
    IN PINFO_CMP_PROC               InfoEqualCB OPTIONAL,
    OUT PIPX_INFO_BLOCK_HEADER     *pNewBlock
) 
{
    ULONG                    i, entriesCount = 1;
    PIPX_TOC_ENTRY           pTocEntry;
    PIPX_INFO_BLOCK_HEADER   pBlock;
    ULONG                    newBlockSize = 
                                InfoSize + sizeof( IPX_INFO_BLOCK_HEADER );
    BOOLEAN                  done = FALSE;
    DWORD                    rc;


    if ( pOldBlock != NULL ) 
    {
        ASSERT (pOldBlock->Version==IPX_ROUTER_VERSION_1);
        
        for ( i=0, pTocEntry = pOldBlock->TocEntry;
              i < pOldBlock->TocEntriesCount;
              i++, pTocEntry++) 
        {
            newBlockSize += pTocEntry->InfoSize*pTocEntry->Count;
            
            if (pTocEntry->InfoType == InfoType) 
            {
                ULONG    j;
                LPBYTE    pInfo = (LPBYTE)pOldBlock+pTocEntry->Offset;

                ASSERT (pTocEntry->InfoSize == InfoSize);
                
                for (j=0; j<pTocEntry->Count; j++, pInfo+=InfoSize) 
                {
                    BOOL    found;
                    
                    if (InfoEqualCB!=NULL)
                    {
                        found = (*InfoEqualCB) (pInfo, Info);
                    }
                    else
                    {
                        found = memcmp (pInfo, Info, InfoSize)==0;
                    }
                    
                    if (found)
                    {
                        return ERROR_ALREADY_EXISTS;
                    }
                }
            }
            
            else 
            {
                entriesCount += 1;
                newBlockSize += sizeof (IPX_TOC_ENTRY);
            }
        }
    }
    
    pBlock = (PIPX_INFO_BLOCK_HEADER)GlobalAlloc (GPTR, newBlockSize);
    
    if ( pBlock != NULL ) 
    {
        ULONG   dstOffset = 
                    FIELD_OFFSET (IPX_INFO_BLOCK_HEADER,TocEntry[entriesCount]);
                    
        PIPX_TOC_ENTRY    dstToc = pBlock->TocEntry;


        pBlock->Version = IPX_ROUTER_VERSION_1;
        pBlock->Size = newBlockSize;
        pBlock->TocEntriesCount = entriesCount;
        
        if (pOldBlock!=NULL) 
        {
            for (i=0, pTocEntry = pOldBlock->TocEntry; 
                 i<pOldBlock->TocEntriesCount; i++, pTocEntry++) 
            {
                *dstToc = *pTocEntry;
                dstToc->Offset = dstOffset;

                memcpy ((PUCHAR)pBlock+dstOffset,
                        (PUCHAR)pOldBlock+pTocEntry->Offset,
                        pTocEntry->InfoSize*pTocEntry->Count);

                dstOffset += dstToc->InfoSize*dstToc->Count;

                if (dstToc->InfoType==InfoType) 
                {
                    memcpy ((PUCHAR)pBlock+dstOffset, Info, InfoSize);
                    dstToc->Count += 1;
                    dstOffset += InfoSize;
                    done = TRUE;
                }
                dstToc += 1;
            }
        }

        if (!done) 
        {
            dstToc->InfoType = InfoType;
            dstToc->InfoSize = InfoSize;
            dstToc->Count = 1;
            dstToc->Offset = dstOffset;
            memcpy ((PUCHAR)pBlock+dstOffset, Info, InfoSize);
        }
        
        *pNewBlock = pBlock;
        rc = NO_ERROR;
    }
    else
    {
        rc = ERROR_NOT_ENOUGH_MEMORY;
    }
    
    return rc;
}

DWORD
DeleteIPXInfoEntry (
    IN PIPX_INFO_BLOCK_HEADER   pOldBlock,
    IN ULONG                    InfoType,
    IN ULONG                    InfoSize,
    IN PVOID                    Info,
    IN PINFO_CMP_PROC           InfoEqualCB OPTIONAL,
    IN PIPX_INFO_BLOCK_HEADER  *pNewBlock
)
{
    ULONG           i, entriesCount = 1, j;
    PIPX_TOC_ENTRY  pTocEntry, dstToc;
    ULONG           newBlockSize = sizeof (IPX_INFO_BLOCK_HEADER)-InfoSize;
    ULONG           dstOffset;
    BOOLEAN         found = FALSE;
    

    ASSERT (pOldBlock->Version==IPX_ROUTER_VERSION_1);

    for ( i=0, pTocEntry = pOldBlock->TocEntry;
          i<pOldBlock->TocEntriesCount;
          i++, pTocEntry++) 
    {
        newBlockSize += pTocEntry->InfoSize*pTocEntry->Count;
        
        if (pTocEntry->InfoType == InfoType) 
        {
            LPBYTE    pInfo = (LPBYTE)pOldBlock+pTocEntry->Offset;

            ASSERT (pTocEntry->InfoSize == InfoSize);
            
            for (j=0; j<pTocEntry->Count; j++, pInfo+=InfoSize) 
            {
                if ( InfoEqualCB != NULL )
                {
                    found = (BOOLEAN) (*InfoEqualCB) (pInfo, Info);
                }
                else
                {
                    found = memcmp (pInfo, Info, InfoSize)==0;
                }
                
                if (found) 
                {
                    if (pTocEntry->Count==1) 
                    {
                        entriesCount -= 1;
                        newBlockSize -= sizeof (IPX_TOC_ENTRY);
                    }
                    break;
                }
            }

            if (!found)
            {
                return ERROR_FILE_NOT_FOUND;
            }
        }

        else 
        {
            entriesCount += 1;
            newBlockSize += sizeof (IPX_TOC_ENTRY);
        }
    }

    if (!found)
    {
        return ERROR_FILE_NOT_FOUND;
    }

    for ( i=0, dstToc = pTocEntry = pOldBlock->TocEntry; 
          i < pOldBlock->TocEntriesCount; i++, pTocEntry++) 
    {
        if (pTocEntry->InfoType==InfoType) 
        {
            if (pTocEntry->Count>1) 
            {
                pTocEntry->Count -= 1;
                dstToc += 1;
            }
        }
        else
        {
            if (dstToc!=pTocEntry) 
            {
                ASSERT (dstToc<pTocEntry);
                *dstToc = *pTocEntry;
            }
            dstToc += 1;
        }
    }

    dstOffset = FIELD_OFFSET (IPX_INFO_BLOCK_HEADER,TocEntry[entriesCount]);
    
    for (i=0, pTocEntry = pOldBlock->TocEntry; 
         i<entriesCount; i++, pTocEntry++) 
    {
        if (pTocEntry->InfoType==InfoType) 
        {
            ULONG    newInfoSize = InfoSize*j;

            if ( j > 0 )
            {
                if (dstOffset!=pTocEntry->Offset) 
                {
                    ASSERT (dstOffset<pTocEntry->Offset);
                    memmove ((PUCHAR)pOldBlock+dstOffset,
                            (PUCHAR)pOldBlock+pTocEntry->Offset,
                            newInfoSize);
                }
            }

            if ( j < pTocEntry->Count )
            {
                memmove ((PUCHAR)pOldBlock+dstOffset+newInfoSize,
                        (PUCHAR)pOldBlock+pTocEntry->Offset+newInfoSize+InfoSize,
                        InfoSize*(pTocEntry->Count-j));
                newInfoSize += InfoSize*(pTocEntry->Count-j);
            }
            
            pTocEntry->Offset = dstOffset;
            dstOffset += newInfoSize;
        }
        else
        {
            if (dstOffset!=pTocEntry->Offset) 
            {
                ASSERT (dstOffset<pTocEntry->Offset);
                memmove ((PUCHAR)pOldBlock+dstOffset,
                        (PUCHAR)pOldBlock+pTocEntry->Offset,
                        pTocEntry->InfoSize*pTocEntry->Count);
                pTocEntry->Offset = dstOffset;
            }
            dstOffset += pTocEntry->InfoSize*pTocEntry->Count;
        }
    }

    pOldBlock->Size = newBlockSize;
    pOldBlock->TocEntriesCount = entriesCount;

    *pNewBlock = pOldBlock;
    return NO_ERROR;
}
    
DWORD
UpdateIPXInfoEntry (
    IN PIPX_INFO_BLOCK_HEADER   pOldBlock,
    IN ULONG                    InfoType,
    IN ULONG                    InfoSize,
    IN PVOID                    OldInfo     OPTIONAL,
    IN PVOID                    NewInfo,
    IN PINFO_CMP_PROC           InfoEqualCB OPTIONAL,
    OUT PIPX_INFO_BLOCK_HEADER *pNewBlock
    ) 
{
    ULONG                    i, j, entriesCount = 1;
    PIPX_TOC_ENTRY           pTocEntry;
    PIPX_INFO_BLOCK_HEADER   pBlock;
    ULONG                    newBlockSize = 
                                InfoSize+sizeof (IPX_INFO_BLOCK_HEADER);
    BOOLEAN                  done = FALSE;
    DWORD                    rc;



    ASSERT (pOldBlock->Version==IPX_ROUTER_VERSION_1);

    for ( i=0, pTocEntry = pOldBlock->TocEntry;
          i < pOldBlock->TocEntriesCount;
          i++, pTocEntry++) 
    {
        if (pTocEntry->InfoType == InfoType) 
        {
            LPBYTE    pInfo = (LPBYTE)pOldBlock+pTocEntry->Offset;
            
            if (OldInfo!=NULL) 
            {
                ASSERT (pTocEntry->InfoSize == InfoSize);
                
                for (j=0; j<pTocEntry->Count; j++, pInfo+=InfoSize) 
                {
                    BOOLEAN    found;

                    if (InfoEqualCB!=NULL)
                    {
                        found = (BOOLEAN) (*InfoEqualCB) (pInfo, OldInfo);
                    }
                    else
                    {
                        found = memcmp (pInfo, OldInfo, InfoSize)==0;
                    }

                    if (found) 
                    {
                        memcpy (pInfo, NewInfo, InfoSize);
                        *pNewBlock = pOldBlock;
                        return NO_ERROR;
                    }
                }
            }
            
            else
            {
                ASSERT (pTocEntry->Count==1);

                if (pTocEntry->InfoSize==InfoSize) 
                {
                    memcpy (pInfo, NewInfo, InfoSize);
                    *pNewBlock = pOldBlock;
                    return NO_ERROR;
                }    

                newBlockSize -= pTocEntry->InfoSize+sizeof (IPX_INFO_BLOCK_HEADER);
            }
        }
        else 
        {
            entriesCount += 1;
            newBlockSize += sizeof (IPX_TOC_ENTRY)+pTocEntry->InfoSize*pTocEntry->Count;
        }
    }

    
    pBlock = (PIPX_INFO_BLOCK_HEADER)GlobalAlloc (GPTR, newBlockSize);

    if ( pBlock != NULL ) 
    {
        ULONG    dstOffset = FIELD_OFFSET (IPX_INFO_BLOCK_HEADER,TocEntry[entriesCount]);
        PIPX_TOC_ENTRY    dstToc = pBlock->TocEntry;

        pBlock->Version = IPX_ROUTER_VERSION_1;
        pBlock->Size = newBlockSize;
        pBlock->TocEntriesCount = entriesCount;
        
        for (i=0, pTocEntry = pOldBlock->TocEntry; 
             i<pOldBlock->TocEntriesCount; i++, pTocEntry++) 
        {
            *dstToc = *pTocEntry;
            dstToc->Offset = dstOffset;

            if (dstToc->InfoType==InfoType) 
            {
                if (pTocEntry->InfoSize==InfoSize) 
                {
                    memcpy ((PUCHAR)pBlock+dstOffset,
                        (PUCHAR)pOldBlock+pTocEntry->Offset,
                        pTocEntry->InfoSize*pTocEntry->Count);

                    dstOffset += dstToc->InfoSize*dstToc->Count;

                    memcpy ((PUCHAR)pBlock+dstOffset, NewInfo, InfoSize);

                    dstOffset += InfoSize;
                    dstToc->Count += 1;
                }
                else
                {
                    memcpy ((PUCHAR)pBlock+dstOffset, NewInfo, InfoSize);
                    dstToc->InfoSize = InfoSize;
                    dstOffset += InfoSize;
                }
                
                done = TRUE;
            }
            else
            {
                memcpy ((PUCHAR)pBlock+dstOffset,
                    (PUCHAR)pOldBlock+pTocEntry->Offset,
                    pTocEntry->InfoSize*pTocEntry->Count);

                dstOffset += dstToc->InfoSize*dstToc->Count;
            }
            
            dstToc += 1;
        }

        if (!done) 
        {
            dstToc->InfoType = InfoType;
            dstToc->InfoSize = InfoSize;
            dstToc->Count = 1;
            dstToc->Offset = dstOffset;
            memcpy ((PUCHAR)pBlock+dstOffset, NewInfo, InfoSize);
        }
        
        *pNewBlock = pBlock;
        rc = NO_ERROR;
    }
    
    else
    {
        rc = ERROR_NOT_ENOUGH_MEMORY;
    }
    
    return rc;
}



DWORD
UpdateRipFilter (
    IN    PIPX_INFO_BLOCK_HEADER    pOldBlock,
    IN    BOOLEAN                   Output, 
    IN    PRIP_ROUTE_FILTER_INFO    pOldFilter OPTIONAL,
    IN    PRIP_ROUTE_FILTER_INFO    pNewFilter OPTIONAL,
    OUT   PIPX_INFO_BLOCK_HEADER   *pNewBlock
    )
{
    ULONG                   i,j;
    PIPX_TOC_ENTRY          pTocEntry, dstToc;
    PIPX_INFO_BLOCK_HEADER  pBlock;
    ULONG                   newBlockSize = FIELD_OFFSET (IPX_INFO_BLOCK_HEADER, TocEntry);
    BOOLEAN                 found = FALSE;
    PRIP_ROUTE_FILTER_INFO  pRfInfo;
    ULONG                   supplyCount, listenCount, count, newCount;
    PRIP_IF_CONFIG          pRipCfg;
    ULONG                   dstOffset;

    if (pOldBlock == NULL)
    {
        return ERROR_INVALID_PARAMETER;
    }

    ASSERT (pOldBlock->Version==IPX_ROUTER_VERSION_1);


    for ( i = 0, pTocEntry = pOldBlock->TocEntry;
          i < pOldBlock->TocEntriesCount;
          i++, pTocEntry++ ) 
    {
        if ( pTocEntry->InfoType == IPX_PROTOCOL_RIP )
        {
            found = TRUE;
            pRipCfg = (PRIP_IF_CONFIG)((LPBYTE)pOldBlock+pTocEntry->Offset);
            
            supplyCount = pRipCfg->RipIfFilters.SupplyFilterCount;
            listenCount = pRipCfg->RipIfFilters.ListenFilterCount;

            if (Output) 
            {
                pRfInfo = &pRipCfg->RipIfFilters.RouteFilter[0];
                count = supplyCount;
            }
            else
            {
                pRfInfo = &pRipCfg->RipIfFilters.RouteFilter[
                                pRipCfg->RipIfFilters.SupplyFilterCount];
                count = listenCount;
            }
            
            newCount = count;

            if (ARGUMENT_PRESENT (pNewFilter)) 
            {
                for (j=0; j<count; j++) 
                {
                    if (memcmp (&pRfInfo[j],pNewFilter,sizeof (*pNewFilter))==0)
                    {
                        return ERROR_ALREADY_EXISTS;
                    }
                }
                
                newBlockSize += sizeof (*pNewFilter);
                newCount += 1;
            }

            if (ARGUMENT_PRESENT (pOldFilter)) 
            {
                for (j=0; j<count; j++) 
                {
                    if (memcmp (&pRfInfo[j],pOldFilter,sizeof (*pOldFilter))==0)
                    {
                        break;
                    }
                }
                
                if (j>=count)
                {
                    return ERROR_FILE_NOT_FOUND;
                }
                
                newBlockSize -= sizeof (*pNewFilter);
                newCount -= 1;
            }
            
            else
            {
                j = count;
            }
        }

        newBlockSize += sizeof (IPX_TOC_ENTRY)+pTocEntry->InfoSize*pTocEntry->Count;
    }
    

    if (!found)
    {
        return ERROR_FILE_NOT_FOUND;
    }


    if ( (newBlockSize>pOldBlock->Size) || 
         !ARGUMENT_PRESENT (pOldFilter)) 
    {
        pBlock = (PIPX_INFO_BLOCK_HEADER)GlobalAlloc (GPTR, newBlockSize);
        
        if (pBlock==NULL)
        {
            return ERROR_NOT_ENOUGH_MEMORY;
        }
        
        pBlock->Version = IPX_ROUTER_VERSION_1;
        pBlock->TocEntriesCount = pOldBlock->TocEntriesCount;
        dstToc = pBlock->TocEntry;
    }
    
    else
    {
        pBlock = pOldBlock;
    }

    dstOffset = FIELD_OFFSET(
                    IPX_INFO_BLOCK_HEADER, TocEntry[pBlock->TocEntriesCount]
                    );
                    
    for (i=0, pTocEntry = pOldBlock->TocEntry;
         i<pOldBlock->TocEntriesCount;
         i++, pTocEntry++, dstToc++) 
    {
        if (pTocEntry->InfoType == IPX_PROTOCOL_RIP) 
        {
            ULONG    curOffset = 
                        FIELD_OFFSET (RIP_IF_CONFIG, RipIfFilters.RouteFilter);
                        
            if (pBlock!=pOldBlock) 
            {
                memcpy ((LPBYTE)pBlock+dstOffset,
                        pRipCfg,
                        curOffset);
            }
            
            else if (dstOffset!=pTocEntry->Offset) 
            {
                ASSERT (dstOffset<pTocEntry->Offset);
                memmove ((LPBYTE)pBlock+dstOffset,
                        pRipCfg,
                        curOffset);
            }


            if (Output) 
            {
                if (j>0) 
                {
                    if (pBlock!=pOldBlock) 
                    {
                        memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                                            pRfInfo, j*sizeof (pRfInfo[0]));
                    }
                    else if (dstOffset!=pTocEntry->Offset) 
                    {
                        ASSERT (dstOffset<pTocEntry->Offset);
                        memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                                            pRfInfo, j*sizeof (pRfInfo[0]));
                    }
                    
                    curOffset += j*sizeof (pRfInfo[0]);
                }
                
                if (ARGUMENT_PRESENT (pNewFilter)) 
                {
                    memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                            pNewFilter,
                            sizeof (*pNewFilter));
                    curOffset += sizeof (*pNewFilter);
                }

                if (ARGUMENT_PRESENT (pOldFilter))
                {
                    j += 1;
                }


                if (j<count) 
                {
                    if (pBlock!=pOldBlock) 
                    {
                        memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                                    &pRfInfo[j], (count-j)*sizeof (pRfInfo[0]));
                    }
                    
                    else if ( (dstOffset!=pTocEntry->Offset) ||
                              !ARGUMENT_PRESENT (pNewFilter)) 
                    {
                        ASSERT (dstOffset<= pTocEntry->Offset);
                        memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                                    &pRfInfo[j], (count-j)*sizeof (pRfInfo[0]));
                    }
                    
                    curOffset += (count-j)*sizeof (pRfInfo[0]);
                }

                
                if (pBlock!=pOldBlock) 
                {
                    memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                            &pRipCfg->RipIfFilters.RouteFilter[supplyCount],
                            listenCount*sizeof (pRipCfg->RipIfFilters.RouteFilter[0]));
                }

                else if ( (dstOffset!=pTocEntry->Offset) ||
                          !ARGUMENT_PRESENT (pNewFilter)) 
                {
                    memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                            &pRipCfg->RipIfFilters.RouteFilter[supplyCount],
                            listenCount*sizeof (pRipCfg->RipIfFilters.RouteFilter[0]));
                }
                
                curOffset += listenCount*sizeof (pRipCfg->RipIfFilters.RouteFilter[0]);
                
                ((PRIP_IF_CONFIG)((LPBYTE)pBlock+dstOffset))->RipIfFilters.SupplyFilterCount = newCount;
                
                if ((newCount==1) && (count==0))
                {
                    ((PRIP_IF_CONFIG)((LPBYTE)pBlock+dstOffset))->RipIfFilters.SupplyFilterAction = IPX_ROUTE_FILTER_DENY;
                }
            }
            
            else 
            {
                if (pBlock!=pOldBlock) 
                {
                    memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                            &pRipCfg->RipIfFilters.RouteFilter[0],
                            supplyCount*sizeof (pRipCfg->RipIfFilters.RouteFilter[0]));
                }
                
                else if (dstOffset!=pTocEntry->Offset) 
                {
                    ASSERT (dstOffset<pTocEntry->Offset);
                    memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                            &pRipCfg->RipIfFilters.RouteFilter[0],
                            supplyCount*sizeof (pRipCfg->RipIfFilters.RouteFilter[0]));
                }
                
                curOffset += supplyCount*sizeof (pRipCfg->RipIfFilters.RouteFilter[0]);
                
                if (j>0) 
                {
                    if (pBlock!=pOldBlock) 
                    {
                        memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                                            pRfInfo, j*sizeof (pRfInfo[0]));
                    }

                    else if (dstOffset!=pTocEntry->Offset) 
                    {
                        ASSERT (dstOffset<pTocEntry->Offset);
                        memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                                            pRfInfo, j*sizeof (pRfInfo[0]));
                    }
                    
                    curOffset += j*sizeof (pRfInfo[0]);
                }
                

                if (ARGUMENT_PRESENT (pNewFilter)) 
                {
                    memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                            pNewFilter,
                            sizeof (*pNewFilter));
                    curOffset += sizeof (*pNewFilter);
                }


                if (ARGUMENT_PRESENT (pOldFilter))
                {
                    j += 1;
                }

                if (j<count) 
                {
                    if (pBlock!=pOldBlock) 
                    {
                        memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                                    &pRfInfo[j], (count-j)*sizeof (pRfInfo[0]));
                    }
                    
                    else if ( (dstOffset!=pTocEntry->Offset) || 
                              !ARGUMENT_PRESENT (pNewFilter)) 
                    {
                        ASSERT (dstOffset<=pTocEntry->Offset);
                        memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                                    &pRfInfo[j], (count-j)*sizeof (pRfInfo[0]));
                    }
                    
                    curOffset += (count-j)*sizeof (pRfInfo[0]);
                }
                
                ((PRIP_IF_CONFIG)((LPBYTE)pBlock+dstOffset))->RipIfFilters.ListenFilterCount = newCount;
                
                if ((newCount==1) && (count==0))
                {
                    ((PRIP_IF_CONFIG)((LPBYTE)pBlock+dstOffset))->RipIfFilters.ListenFilterAction = IPX_ROUTE_FILTER_DENY;
                }
            }

            if (pBlock!=pOldBlock) 
            {
                *dstToc = *pTocEntry;
                dstToc->Offset = dstOffset;
                dstToc->InfoSize = curOffset;
            }
            
            else 
            {
                pTocEntry->Offset = dstOffset;
                pTocEntry->InfoSize = curOffset;
            }

            dstOffset += curOffset;
        }
        else
        {
            if (pBlock!=pOldBlock) 
            {
                memcpy ((PUCHAR)pBlock+dstOffset,
                    (PUCHAR)pOldBlock+pTocEntry->Offset,
                    pTocEntry->InfoSize*pTocEntry->Count);
                *dstToc = *pTocEntry;
                dstToc->Offset = dstOffset;
            }
            
            else if (dstOffset!=pTocEntry->Offset) 
            {
                ASSERT (dstOffset<pTocEntry->Offset);
                memmove ((PUCHAR)pBlock+dstOffset,
                    (PUCHAR)pOldBlock+pTocEntry->Offset,
                    pTocEntry->InfoSize*pTocEntry->Count);
                pTocEntry->Offset = dstOffset;
            }
            
            dstOffset += pTocEntry->InfoSize*pTocEntry->Count;
        }
    }

    pBlock->Size = newBlockSize;
    *pNewBlock = pBlock;
    return NO_ERROR;
}



DWORD
UpdateSapFilter (
    IN    PIPX_INFO_BLOCK_HEADER    pOldBlock,
    IN    BOOLEAN                   Output, 
    IN    PSAP_SERVICE_FILTER_INFO  pOldFilter OPTIONAL,
    IN    PSAP_SERVICE_FILTER_INFO  pNewFilter OPTIONAL,
    OUT   PIPX_INFO_BLOCK_HEADER   *pNewBlock
    ) 
{
    ULONG                           i,j;
    PIPX_TOC_ENTRY                  pTocEntry, dstToc;
    PIPX_INFO_BLOCK_HEADER          pBlock;
    ULONG                           newBlockSize = FIELD_OFFSET (IPX_INFO_BLOCK_HEADER, TocEntry);
    BOOLEAN                         found = FALSE;
    PSAP_SERVICE_FILTER_INFO        pSfInfo;
    ULONG                           supplyCount, listenCount, count, newCount;
    PSAP_IF_CONFIG                  pSapCfg;
    ULONG                           dstOffset;

    if (pOldBlock == NULL)
    {
        return ERROR_INVALID_PARAMETER;
    }

    ASSERT (pOldBlock->Version==IPX_ROUTER_VERSION_1);
    

    for ( i=0, pTocEntry = pOldBlock->TocEntry;
          i<pOldBlock->TocEntriesCount;
          i++, pTocEntry++) 
    {
        if (pTocEntry->InfoType == IPX_PROTOCOL_SAP)
        {
            found = TRUE;
            pSapCfg = (PSAP_IF_CONFIG)((LPBYTE)pOldBlock+pTocEntry->Offset);
            
            supplyCount = pSapCfg->SapIfFilters.SupplyFilterCount;
            listenCount = pSapCfg->SapIfFilters.ListenFilterCount;

            if (Output) 
            {
                pSfInfo = &pSapCfg->SapIfFilters.ServiceFilter[0];
                count = supplyCount;
            }
            else 
            {
                pSfInfo = &pSapCfg->SapIfFilters.ServiceFilter[
                                pSapCfg->SapIfFilters.SupplyFilterCount];
                count = listenCount;
            }
            
            newCount = count;

            if (ARGUMENT_PRESENT (pNewFilter)) 
            {
                for (j=0; j<count; j++) 
                {
                    if ((pSfInfo[j].ServiceType==pNewFilter->ServiceType)
                            && (strncmp ((LPSTR)pSfInfo[j].ServiceName, 
                                    (LPSTR)pNewFilter->ServiceName,
                                    sizeof (pNewFilter->ServiceName))==0))
                        break;
                }
                
                if (j<count)
                {
                    return ERROR_CAN_NOT_COMPLETE;
                }
                
                newBlockSize += sizeof (*pNewFilter);
                newCount += 1;
            }

            if (ARGUMENT_PRESENT (pOldFilter)) 
            {
                for (j=0; j<count; j++) 
                {
                    if ((pSfInfo[j].ServiceType==pOldFilter->ServiceType)
                            && (strncmp ((LPSTR)pSfInfo[j].ServiceName, 
                                    (LPSTR)pOldFilter->ServiceName,
                                    sizeof (pOldFilter->ServiceName))==0))
                        break;
                }
                
                if (j>=count)
                {
                    return ERROR_CAN_NOT_COMPLETE;
                }
                
                newBlockSize -= sizeof (*pNewFilter);
                newCount -= 1;
            }
            
            else
            {
                j = count;
            }
        }

        newBlockSize += sizeof (IPX_TOC_ENTRY)+pTocEntry->InfoSize*pTocEntry->Count;
    }
    

    if (!found)
    {
        return ERROR_CAN_NOT_COMPLETE;
    }


    if ((newBlockSize>pOldBlock->Size) || !ARGUMENT_PRESENT (pOldFilter)) 
    {
        pBlock = (PIPX_INFO_BLOCK_HEADER)GlobalAlloc (GPTR, newBlockSize);
        
        if (pBlock==NULL)
        {
            return ERROR_NOT_ENOUGH_MEMORY;
        }
        
        pBlock->Version = IPX_ROUTER_VERSION_1;
        pBlock->TocEntriesCount = pOldBlock->TocEntriesCount;
        dstToc = pBlock->TocEntry;
    }
    
    else
    {
        pBlock = pOldBlock;
    }

    dstOffset = FIELD_OFFSET (IPX_INFO_BLOCK_HEADER, TocEntry[pBlock->TocEntriesCount]);
    
    for ( i=0, pTocEntry = pOldBlock->TocEntry;
          i < pOldBlock->TocEntriesCount;
          i++, pTocEntry++, dstToc++) 
    {
        if (pTocEntry->InfoType == IPX_PROTOCOL_SAP) 
        {
            ULONG    curOffset = FIELD_OFFSET (SAP_IF_CONFIG, SapIfFilters.ServiceFilter);
            
            if (pBlock!=pOldBlock) 
            {
                memcpy ((LPBYTE)pBlock+dstOffset,
                        pSapCfg,
                        curOffset);
            }
            else if (dstOffset!=pTocEntry->Offset) 
            {
                ASSERT (dstOffset<pTocEntry->Offset);
                memmove ((LPBYTE)pBlock+dstOffset,
                        pSapCfg,
                        curOffset);
            }


            if (Output) 
            {
                if (j>0) 
                {
                    if (pBlock!=pOldBlock) 
                    {
                        memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                                        pSfInfo, j*sizeof (pSfInfo[0]));
                    }
                    
                    else if (dstOffset!=pTocEntry->Offset) 
                    {
                        ASSERT (dstOffset<pTocEntry->Offset);
                        memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                                        pSfInfo, j*sizeof (pSfInfo[0]));
                    }
                    
                    curOffset += j*sizeof (pSfInfo[0]);
                }
                
                if (ARGUMENT_PRESENT (pNewFilter)) 
                {
                    memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                            pNewFilter,
                            sizeof (*pNewFilter));
                    curOffset += sizeof (*pNewFilter);
                }

                if (ARGUMENT_PRESENT (pOldFilter))
                {
                    j += 1;
                }


                if (j<count) 
                {
                    if (pBlock!=pOldBlock) 
                    {
                        memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                                    &pSfInfo[j], (count-j)*sizeof (pSfInfo[0]));
                    }
                    
                    else if ((dstOffset!=pTocEntry->Offset) || !ARGUMENT_PRESENT (pNewFilter)) 
                    {
                        ASSERT (dstOffset<=pTocEntry->Offset);
                        memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                                    &pSfInfo[j], (count-j)*sizeof (pSfInfo[0]));
                    }
                    
                    curOffset += (count-j)*sizeof (pSfInfo[0]);
                }
                
                if (pBlock!=pOldBlock) 
                {
                    memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                            &pSapCfg->SapIfFilters.ServiceFilter[supplyCount],
                            listenCount*sizeof (pSapCfg->SapIfFilters.ServiceFilter[0]));
                }
                
                else if ((dstOffset!=pTocEntry->Offset) || !ARGUMENT_PRESENT (pNewFilter)) 
                {
                    ASSERT (dstOffset<=pTocEntry->Offset);
                    memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                            &pSapCfg->SapIfFilters.ServiceFilter[supplyCount],
                            listenCount*sizeof (pSapCfg->SapIfFilters.ServiceFilter[0]));
                }
                
                curOffset += listenCount*sizeof (pSapCfg->SapIfFilters.ServiceFilter[0]);
                ((PSAP_IF_CONFIG)((LPBYTE)pBlock+dstOffset))->SapIfFilters.SupplyFilterCount = newCount;
                
                if ((newCount==1) && (count==0))
                {
                    ((PSAP_IF_CONFIG)((LPBYTE)pBlock+dstOffset))->SapIfFilters.SupplyFilterAction = IPX_SERVICE_FILTER_DENY;
                }
            }
            
            else 
            {
                if (pBlock!=pOldBlock) 
                {
                    memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                            &pSapCfg->SapIfFilters.ServiceFilter[0],
                            supplyCount*sizeof (pSapCfg->SapIfFilters.ServiceFilter[0]));
                }
                
                else if (dstOffset!=pTocEntry->Offset) 
                {
                    ASSERT (dstOffset<pTocEntry->Offset);
                    memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                            &pSapCfg->SapIfFilters.ServiceFilter[0],
                            supplyCount*sizeof (pSapCfg->SapIfFilters.ServiceFilter[0]));
                }
                
                curOffset += supplyCount*sizeof (pSapCfg->SapIfFilters.ServiceFilter[0]);
                
                if (j>0)
                {
                    if (pBlock!=pOldBlock) 
                    {
                        memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                                        pSfInfo, j*sizeof (pSfInfo[0]));
                    }
                    
                    else if (dstOffset!=pTocEntry->Offset) 
                    {
                        ASSERT (dstOffset<pTocEntry->Offset);
                        memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                                        pSfInfo, j*sizeof (pSfInfo[0]));
                    }
                    
                    curOffset += j*sizeof (pSfInfo[0]);
                }

                if (ARGUMENT_PRESENT (pNewFilter)) 
                {
                    memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                            pNewFilter,
                            sizeof (*pNewFilter));
                    curOffset += sizeof (*pNewFilter);
                }

                if (ARGUMENT_PRESENT (pOldFilter))
                {
                    j += 1;
                }

                if (j<count) 
                {
                    if (pBlock!=pOldBlock) 
                    {
                        memcpy ((LPBYTE)pBlock+dstOffset+curOffset,
                                    &pSfInfo[j], (count-j)*sizeof (pSfInfo[0]));
                    }
                    
                    else if ( (dstOffset!=pTocEntry->Offset) || 
                              !ARGUMENT_PRESENT (pNewFilter)) 
                    {
                        ASSERT (dstOffset<=pTocEntry->Offset);
                        memmove ((LPBYTE)pBlock+dstOffset+curOffset,
                                    &pSfInfo[j], (count-j)*sizeof (pSfInfo[0]));
                    }
                    
                    curOffset += (count-j)*sizeof (pSfInfo[0]);
                }
                
                ((PSAP_IF_CONFIG)((LPBYTE)pBlock+dstOffset))->SapIfFilters.ListenFilterCount = newCount;
                
                if ((newCount==1) && (count==0))
                {
                    ((PSAP_IF_CONFIG)((LPBYTE)pBlock+dstOffset))->SapIfFilters.ListenFilterAction = IPX_SERVICE_FILTER_DENY;
                }
            }

            if (pBlock!=pOldBlock) 
            {
                *dstToc = *pTocEntry;
                dstToc->Offset = dstOffset;
                dstToc->InfoSize = curOffset;
            }

            else 
            {
                pTocEntry->Offset = dstOffset;
                pTocEntry->InfoSize = curOffset;
            }

            dstOffset += curOffset;
        }
        
        else 
        {
            if (pBlock!=pOldBlock) 
            {
                memcpy ((PUCHAR)pBlock+dstOffset,
                    (PUCHAR)pOldBlock+pTocEntry->Offset,
                    pTocEntry->InfoSize*pTocEntry->Count);
                *dstToc = *pTocEntry;
                dstToc->Offset = dstOffset;
            }
            
            else if (dstOffset!=pTocEntry->Offset) 
            {
                ASSERT (dstOffset<pTocEntry->Offset);
                memmove ((PUCHAR)pBlock+dstOffset,
                    (PUCHAR)pOldBlock+pTocEntry->Offset,
                    pTocEntry->InfoSize*pTocEntry->Count);
                pTocEntry->Offset = dstOffset;
            }
            
            dstOffset += pTocEntry->InfoSize*pTocEntry->Count;
        }
    }

    pBlock->Size = newBlockSize;
    *pNewBlock = pBlock;
    return NO_ERROR;
}