Click here to Skip to main content
15,885,366 members
Articles / Containers / Virtual Machine

Writing a Parental Control Software for Windows

Rate me:
Please Sign up or sign in to vote.
4.78/5 (9 votes)
8 Jul 2010GPL39 min read 57K   2.7K   28  
Writing a parental control software: The beginning of the adventure
//   This file is part of the EasyParentalControl Project.
//
//   EasyParentalControl is free software: you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   EasyParentalControl is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.

//   You should have received a copy of the GNU General Public License
//   along with EasyParentalControl.  If not, see <http://www.gnu.org/licenses/>.

// Parts of this file is derived from the winsock lsp sample, that can be found in the Microsoft Windows SDK v7.0 

#include "instlsp.h"

//
// Function: RemoveIdFromChain
//
// Description:
//    This function removes the given CatalogId from the protocol chain 
//    for pinfo.
//
BOOL 
RemoveIdFromChain(
    WSAPROTOCOL_INFOW *pInfo, 
    DWORD              dwCatalogId
    )
{
    int     i, 
            j;

    for(i=0; i < pInfo->ProtocolChain.ChainLen ;i++)
    {
        if ( pInfo->ProtocolChain.ChainEntries[ i ] == dwCatalogId )
        {
            for(j=i; j < pInfo->ProtocolChain.ChainLen-1 ; j++)
            {
                pInfo->ProtocolChain.ChainEntries[ j ] = 
                        pInfo->ProtocolChain.ChainEntries[ j+1 ];
            }
            pInfo->ProtocolChain.ChainLen--;
            return TRUE;
        }
    }
    return FALSE;
}

//
// Function: IsIdinChain
//
// Description:
//    This function determines whether the given catalog id is referenced
//    in the protocol chain of pinfo.
//
BOOL 
IsIdInChain(
    WSAPROTOCOL_INFOW *pInfo, 
    DWORD              dwId)
{
    int     i;

    for(i=0; i < pInfo->ProtocolChain.ChainLen ;i++)
    {
        if ( pInfo->ProtocolChain.ChainEntries[ i ] == dwId )
            return TRUE;
    }
    return FALSE;
}

//
// Function: GetProviderCount
//
// Description:
//    Returns the number of providers installed matching the given Provider Type.
//    The possible provider types are BASE_PROTOCOL or LAYERED_PROTOCOL. That is,
//    this function either returns the count of base providers or the number of
//    dummy LSP providers installed.
//
int
GetProviderCount(
    WSAPROTOCOL_INFOW *pProviders,
    int                iProviderCount,
    int                iProviderType
    )
{
    int Count, i;

    Count = 0;
    for(i=0; i < iProviderCount ;i++)
    {
        if ( ( LAYERED_CHAIN == iProviderType ) && ( pProviders[ i ].ProtocolChain.ChainLen > 1 ) )
            Count++;
        else if ( ( LAYERED_CHAIN != iProviderType) && ( pProviders[ i ].ProtocolChain.ChainLen == iProviderType ) )
            Count++;
    }
    return Count;
}

//
// Function: GetLayeredEntriesByGuid
//
// Description:
//    This routine is used by the uninstaller when WSCUpdateProvider is not 
//    available. If after an LSP is removed, there are other LSPs which depend
//    on that LSP, the uninstaller must remove and reinstall all LSPs and fix up
//    the catalog ID references within the protocol chain. This routine is used
//    when reinstalling LSP providers. Because the layered entries belonging to
//    an LSP may be installed either under individual GUIDs or whole
//    groups of providers may be installed under the same GUID. This routine
//    Takes an array of WSAPROTOCOL_INFOW entries belonging to one LSP along with
//    a GUID and copies all WSAPROTOCOL_INFOW structures having the same GUID
//    to an array. The count of how many structures were copied is returned.
//
int
GetLayeredEntriesByGuid(
    WSAPROTOCOL_INFOW *pMatchLayers,
    int               *iLayeredCount,
    WSAPROTOCOL_INFOW *pEntries, 
    int                iEntryCount,
    GUID              *MatchGuid
    )
{
    int                count, 
                       err = SOCKET_ERROR,
                       i;

    // First count how many entries belong to this GUID
    count = 0;
    for(i=0; i < iEntryCount ;i++)
    {
        if ( 0 == memcmp( MatchGuid, &pEntries[i].ProviderId, sizeof( GUID ) ) )
            count++;
    }

    // Make sure the array passed in is large enough to hold the results
    if ( count > *iLayeredCount )
    {
        *iLayeredCount = count;
        goto cleanup;
    }

    // Go back and copy the matching providers into our array
    count = 0;
    for(i=0; i < iEntryCount ;i++)
    {
        if ( 0 == memcmp( MatchGuid, &pEntries[ i ].ProviderId, sizeof( GUID ) ) )
        {
            memcpy( &pMatchLayers[ count++ ], &pEntries[ i ], sizeof( WSAPROTOCOL_INFOW ) );
        }
    }

    *iLayeredCount = count;

    err = NO_ERROR;

cleanup:

    return err;
}

//
// Function: IsEqualProtocolEntries
//
// Description:
//    This routine compares two WSAPROTOCOL_INFOW structures to determine 
//    whether they are equal. This is done when a provider is uninstalled
//    and then reinstalled. After reinstallation we need to match the old
//    provider to the new (after reenumerating the catalog) so we can find
//    its new catalog ID. The fields used for determining a match are all
//    fields except the catalog ID (dwCatalogEntryId) and the protocol
//    string (szProtocol).
//
BOOL
IsEqualProtocolEntries(
    WSAPROTOCOL_INFOW *pInfo1,
    WSAPROTOCOL_INFOW *pInfo2
    )
{
    if ( (memcmp(&pInfo1->ProviderId, &pInfo2->ProviderId, sizeof(GUID)) == 0) &&
         (pInfo1->dwServiceFlags1 == pInfo2->dwServiceFlags1) &&
         (pInfo1->dwServiceFlags2 == pInfo2->dwServiceFlags2) &&
         (pInfo1->dwServiceFlags3 == pInfo2->dwServiceFlags3) &&
         (pInfo1->dwServiceFlags4 == pInfo2->dwServiceFlags4) &&
         (pInfo1->ProtocolChain.ChainLen == pInfo2->ProtocolChain.ChainLen) &&
         (pInfo1->iVersion == pInfo2->iVersion) &&
         (pInfo1->iAddressFamily == pInfo2->iAddressFamily) &&
         (pInfo1->iMaxSockAddr == pInfo2->iMaxSockAddr) &&
         (pInfo1->iMinSockAddr == pInfo2->iMinSockAddr) &&
         (pInfo1->iSocketType == pInfo2->iSocketType) &&
         (pInfo1->iProtocol == pInfo2->iProtocol) &&
         (pInfo1->iProtocolMaxOffset == pInfo2->iProtocolMaxOffset) &&
         (pInfo1->iNetworkByteOrder == pInfo2->iNetworkByteOrder) &&
         (pInfo1->iSecurityScheme == pInfo2->iSecurityScheme) &&
         (pInfo1->dwMessageSize == pInfo2->dwMessageSize)
       )
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

//
// Function: RetrieveLspGuid
//
// Description:
//      This routine loads the given DLL and retrieves its GetLspGuid function
//      which returns the GUID under which the LSP's hidden dummy entry is to be
//      installed under. By exporting this function the uninstaller is decoupled
//      from a specific LSP.
//
int
RetrieveLspGuid(
    __in_z char    *LspPath,
    GUID    *Guid
    )
{
    HMODULE         hMod = NULL;

//	extern "C" int __declspec(dllexport) __stdcall

    LPFN_GETLSPGUID fnGetLspGuid = NULL;
    int             retval = SOCKET_ERROR;

    // Load teh library
    hMod = LoadLibraryA( LspPath );
    if ( NULL == hMod )
    {
        fprintf( stderr, "RetrieveLspGuid: LoadLibraryA failed: %d\n", GetLastError() );
        goto cleanup;
    }

    // Get a pointer to the LSPs GetLspGuid function
    fnGetLspGuid = (LPFN_GETLSPGUID) GetProcAddress( hMod, "GetLspGuid" );
    if ( NULL == fnGetLspGuid )
    {
        fprintf( stderr, "RetrieveLspGuid: GetProcAddress failed: %d\n", GetLastError() );
        goto cleanup;
    }

    // Retrieve the LSPs GUID
    fnGetLspGuid( Guid );

    retval = NO_ERROR;

cleanup:

    if ( NULL != hMod )
        FreeLibrary( hMod );

    return retval;
}


#pragma warning(push)
#pragma warning(disable: 4127)

//
// Function: IsNonIfsProvider
//
// Description:
//      This routine searches the catalog for a given provider (based on catalog ID)
//      and returns whether or not it is an IFS provider. This routine is used when
//      installing an IFS provider since all IFS providers have to appear in the 
//      protocol chain beneath any non-IFS providers.
//
BOOL
IsNonIfsProvider(
    WSAPROTOCOL_INFOW  *pProvider,
    int                 iProviderCount,
    DWORD               dwProviderId
    )
{
    int     i;

    for(i=0; i < iProviderCount ;i++)
    {
        if ( pProvider[ i ].dwCatalogEntryId == dwProviderId )
        {
            return !( pProvider[ i ].dwServiceFlags1 & XP1_IFS_HANDLES );
        }
    }
    
    return FALSE;
}

#pragma warning(pop)

//
// Function: LoadUpdateProviderFunction
//
// Description:
//      This routine loads the WSCUpdateProvider function from ws2_32.dll.
//      This function is used when removing LSP entries and other LSPs are layered
//      on top of the one to be removed. If this API is present (which it is on
//      Windows XP and later), then the logic is much simpler. This routien will
//      load both the 32-bit and 64-bit versions of the function.
//
HMODULE
LoadUpdateProviderFunction()
{
    HMODULE hModule = NULL;
    HRESULT hr;
    char    WinsockLibraryPath[ MAX_PATH+1 ],
            szExpandPath[ MAX_PATH+1 ];

    //
    // See if we're on a platform that supports WSCUpdateProvider. If so then
    // uninstalling an LSP is easy; otherwise, it is very painful if you're
    // removing an LSP that has other LSPs on top if it.
    //
    if ( GetSystemDirectoryA( WinsockLibraryPath, MAX_PATH+1 ) == 0 )
    {
        hr = StringCchCopyA( szExpandPath, MAX_PATH+1, "%SYSTEMROOT%\\system32" );
        if ( FAILED( hr ) )
        {
            fprintf( stderr, "LoadUpdateProviderFunctions: StringCchCopyA failed: 0x%x\n", hr );
            goto cleanup;
        }

        if ( ExpandEnvironmentStringsA( WinsockLibraryPath, szExpandPath, MAX_PATH+1 ) == 0 )
        {
            fprintf(stderr, "LoadUpdateProviderFunctions: Unable to expand environment string: %d\n", 
                    GetLastError()
                   );
            goto cleanup;
        }
    }

    hr = StringCchCatA( WinsockLibraryPath, MAX_PATH+1, WINSOCK_DLL );
    if ( FAILED( hr ) )
    {
        fprintf( stderr, "LoadUpdateProviderFunctions: StringCchCatA failed: 0x%x\n", hr );
        goto cleanup;
    }

    hModule = LoadLibraryA( WinsockLibraryPath );
    if (hModule == NULL)
    {
        fprintf(stderr, "LoadUpdateProviderFunctions: Unable to load %s: %d\n", 
                WinsockLibraryPath, GetLastError()
                );
        goto cleanup;
    }
#ifdef _WIN64
    fnWscUpdateProvider   = (LPWSCUPDATEPROVIDER)GetProcAddress(hModule, "WSCUpdateProvider");

    fnWscUpdateProvider32 = (LPWSCUPDATEPROVIDER)GetProcAddress(hModule, "WSCUpdateProvider32");
#else
    fnWscUpdateProvider   = (LPWSCUPDATEPROVIDER)GetProcAddress(hModule, "WSCUpdateProvider");
#endif

    return hModule;

cleanup:

    if ( NULL != hModule )
    {
        FreeLibrary( hModule );
        hModule = NULL;
    }

    return NULL;
}

//
// Function: CountOrphanedChainEntries
//
// Description:
//      This routne counts how many orphaned layered protocol entries exist. An 
//      orphaned protocol entry is a protocol entry whose chain length greater
//      than one and whose dummy hidden entry (i.e. index 0 of its protocol chain
//      array) is missing. When building the LSP map, it normally finds entries
//      based upon the dummy hidden entries. This method is used to determine
//      if orphaned entries exist so they can be removed as well.
//
int
CountOrphanedChainEntries(
    WSAPROTOCOL_INFOW  *pCatalog,
    int                 iCatalogCount
    )
{
    int     orphanCount = 0,
            i, j;

    for(i=0; i < iCatalogCount ;i++)
    {
        if ( pCatalog[ i ].ProtocolChain.ChainLen > 1 )
        {
            for(j=0; j < iCatalogCount ;j++)
            {
                if ( i == j )
                    continue;
                if ( pCatalog[ j ].dwCatalogEntryId == pCatalog[ i ].ProtocolChain.ChainEntries[ 0 ] )
                {
                    break;
                }
            }
            if ( j >= iCatalogCount )
                orphanCount++;
        }
    }

    return orphanCount;
}

//
// Function: FindProviderById
//
// Description:
//    This routine searches the Winsock catalog for the provider entry which matches
//    the supplied catalog ID.
//
WSAPROTOCOL_INFOW *
FindProviderById(
    DWORD               CatalogId,
    WSAPROTOCOL_INFOW  *Catalog,
    int                 CatalogCount
    )
{
    int     i;

    for(i=0; i < CatalogCount ;i++)
    {
        if ( Catalog[ i ].dwCatalogEntryId == CatalogId )
            return &Catalog[ i ];
    }
    return NULL;
}


//
// Function: FindProviderByGuid
//
// Description:
//    This routine searches the Winsock catalog for the entry which matches the
//    supplied GUID.
//
WSAPROTOCOL_INFOW *
FindProviderByGuid(
    GUID               *Guid,
    WSAPROTOCOL_INFOW  *Catalog,
    int                 CatalogCount
    )
{
    int     i;

    for(i=0; i < CatalogCount ;i++)
    {
        if ( 0 == memcmp( &Catalog[ i ].ProviderId, Guid, sizeof( GUID ) ) )
        {
            return &Catalog[ i ];
        }
    }

    return NULL;
}

//
// Function: GetCatalogIdForProviderGuid
//
// Description:
//    This routine finds the WInsock catalog entry for the GUID and returns the
//    catalog ID for that entry.
//
DWORD
GetCatalogIdForProviderGuid(
    GUID               *Guid,
    WSAPROTOCOL_INFOW  *Catalog,
    int                 CatalogCount
    )
{
    WSAPROTOCOL_INFOW *match = NULL;

    match = FindProviderByGuid( Guid, Catalog, CatalogCount );
    if ( NULL != match )
    {
        return match->dwCatalogEntryId;
    }

    return 0;
}

#pragma warning(push)
#pragma warning(disable: 4127 )

//
// Function: FindDummyIdFromProtocolChain
//
// Description:
//    This routine searches the catalog for the dummy LSP entry associated
//    with the given catalog ID. If the catalog ID pass is actually the 
//    dummy entry, that provider is found and returned. If the ID is a
//    protocol chain, then the first entry in that provider's protocol chain
//    will reference the dummy ID of the LSP so that entry is then found
//    and returned.
//
DWORD
FindDummyIdFromProtocolChainId(
    DWORD               CatalogId,
    WSAPROTOCOL_INFOW  *Catalog,
    int                 CatalogCount
    )
{
    int     i;

    for(i=0; i < CatalogCount ;i++)
    {
        if ( CatalogId == Catalog[ i ].dwCatalogEntryId )
        {
            if ( Catalog[ i ].ProtocolChain.ChainLen == LAYERED_PROTOCOL )
                return Catalog[ i ].dwCatalogEntryId;
            else
                return Catalog[ i ].ProtocolChain.ChainEntries[ 0 ];
        }
    }

    ASSERT( 0 );

    return 0;
}

#pragma warning(pop)

//
// Function: InsertIdIntoProtocolChain
//
// Description:
//    This routine inserts the given ID into an existing catalog entry at
//    the given index location within the chain array.
//
void
InsertIdIntoProtocolChain(
    WSAPROTOCOL_INFOW  *Entry,
    int                 Index,
    DWORD               InsertId
    )
{
    int     i;

    for(i=Entry->ProtocolChain.ChainLen; i > Index ;i--)
    {
        Entry->ProtocolChain.ChainEntries[ i ] = Entry->ProtocolChain.ChainEntries[ i - 1 ];
    }

    Entry->ProtocolChain.ChainEntries[ Index ] = InsertId;
    Entry->ProtocolChain.ChainLen++;
}

//
// Function: BuildSubsetLspChain
//
// Description:
//    This routine takes a portion of an existing protocol chain (from the specified
//    index to the end) and makes it the new protocol chain while inserting the 
//    specified DummyId in index 0. For example, 
//
//       Array Indices:    0      1      2      3
//                      | 1400 | 1301 | 1201 | 1001 |       Len = 4
//
//       If this routine is called with Index == 2 and DummyId == 1500 then
//       the protocol chain would be:
//
//       Array Indices:    0      1      2      3
//                      | 1500 | 1201 | 1001 |      |       Len = 3
//
void
BuildSubsetLspChain(
    WSAPROTOCOL_INFOW  *Entry,
    int                 Index,
    DWORD               DummyId
    )
{
    int     Idx, i;

    for(i=Index,Idx=1; i < Entry->ProtocolChain.ChainLen ;i++,Idx++)
    {
        Entry->ProtocolChain.ChainEntries[ Idx ] = Entry->ProtocolChain.ChainEntries[ i ];
    }

    Entry->ProtocolChain.ChainEntries[ 0 ] = DummyId;
    Entry->ProtocolChain.ChainLen = Entry->ProtocolChain.ChainLen - Index + 1;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer (Senior)
France France
I am a French programmer.
These days I spend most of my time with the .NET framework, JavaScript and html.

Comments and Discussions