/*++

Copyright (c) 1994  Microsoft Corporation

Module Name:

    svcinfo.cxx

Abstract:

    contains class implementation of service location classes.

Author:

    Madan Appiah (madana)  15-May-1995

Environment:

    User Mode - Win32

Revision History:

--*/

#include <svcloc.hxx>

DWORD
EMBED_SERVICE_INFO::ComputeServiceInfoSize(
    LPINET_SERVICE_INFO ServiceInfo
    )
/*++

Routine Description:

    This private member function computes the size of the embedded service
    info buffer.

Arguments:

    ServiceInfo : pointer to a service info structure.

Return Value:

    size of the embedded service info buffer.

--*/
{
    DWORD Size;
    DWORD NumBindings;
    LPINET_BIND_INFO BindingsInfo;
    DWORD i;
    DWORD ServiceCommentLen;

    if( ServiceInfo->ServiceComment != NULL) {
        ServiceCommentLen =
            ROUND_UP_COUNT(
                (strlen(ServiceInfo->ServiceComment) + 1) * sizeof(CHAR),
                            ALIGN_DWORD);
    }
    else {
        ServiceCommentLen = ROUND_UP_COUNT( 1, ALIGN_DWORD );
    }

    Size =
        sizeof(ULONGLONG) + // service mask
        sizeof(INET_SERVICE_STATE) + // service state
        ServiceCommentLen + // service comment
        sizeof(DWORD); // NumBindings

    NumBindings = ServiceInfo->Bindings.NumBindings;
    BindingsInfo = ServiceInfo->Bindings.BindingsInfo;

    if( NumBindings != 0 ) {

        TcpsvcsDbgAssert( BindingsInfo != NULL )

        for( i = 0; i < NumBindings; i++ ) {

            Size += sizeof(DWORD);

            if( BindingsInfo[i].Length != 0 ) {

                Size += ROUND_UP_COUNT(BindingsInfo[i].Length, ALIGN_DWORD);
                TcpsvcsDbgAssert( BindingsInfo[i].BindData != NULL );
            }
        }
    }

    return( Size );
}

VOID
EMBED_SERVICE_INFO::CopyServiceInfo(
    LPINET_SERVICE_INFO ServiceInfo
    )
/*++

Routine Description:

    This private member function copies service info to the embedded service info buffer.

Arguments:

    ServiceInfo : pointer to a service info structure.

Return Value:

    NONE.

--*/
{
    DWORD NumBindings;
    LPINET_BIND_INFO BindingsInfo;
    DWORD i;
    LPBYTE EndofBuffer;
    LPBYTE BufferPtr;

    NumBindings = ServiceInfo->Bindings.NumBindings;
    BindingsInfo = ServiceInfo->Bindings.BindingsInfo;

    BufferPtr = _ServiceInfoBuffer;
    EndofBuffer = _ServiceInfoBuffer + _ServiceInfoLength;

    //
    // copy header.
    //

    *(ULONGLONG *)BufferPtr = ServiceInfo->ServiceMask;
    _ServiceMask = (ULONGLONG *)BufferPtr;
    BufferPtr += sizeof(ULONGLONG);

    *(INET_SERVICE_STATE *)BufferPtr = ServiceInfo->ServiceState;
    _ServiceState = (INET_SERVICE_STATE *)BufferPtr;
    BufferPtr += sizeof(INET_SERVICE_STATE);

    //
    // copy service comment.
    //

    DWORD CommentLen;

    if( ServiceInfo->ServiceComment != NULL) {
        CommentLen =
            ROUND_UP_COUNT(
                (strlen(ServiceInfo->ServiceComment) + 1) * sizeof(CHAR),
                            ALIGN_DWORD);
    }
    else {
        CommentLen = ROUND_UP_COUNT( 1, ALIGN_DWORD );
    }

    TcpsvcsDbgAssert( (BufferPtr + CommentLen) < EndofBuffer );

    if( ServiceInfo->ServiceComment != NULL) {
        strcpy( (LPSTR)BufferPtr, ServiceInfo->ServiceComment );
    }
    else {
        *(LPSTR)BufferPtr = '\0';
    }

    BufferPtr += CommentLen;

    *(DWORD *)BufferPtr = ServiceInfo->Bindings.NumBindings;
    BufferPtr += sizeof(DWORD);

    //
    // copy bindings.
    //

    if( NumBindings != 0 ) {

        for( i = 0; i < NumBindings; i++ ) {

            TcpsvcsDbgAssert( BufferPtr < EndofBuffer );

            *(DWORD *)BufferPtr = BindingsInfo[i].Length;
            BufferPtr += sizeof(DWORD);

            if( BindingsInfo[i].Length != 0 ) {

                memcpy( BufferPtr, BindingsInfo[i].BindData, BindingsInfo[i].Length );
                BufferPtr += ROUND_UP_COUNT( BindingsInfo[i].Length, ALIGN_DWORD );
            }
        }
    }

    TcpsvcsDbgAssert( BufferPtr == EndofBuffer );

    return;
}

EMBED_SERVICE_INFO::EMBED_SERVICE_INFO(
    LPINET_SERVICE_INFO ServiceInfo
    )
/*++

Routine Description:

    This function constructs an embedded service info object.

    Note : embedded service info buffer layout :

    dword 1 : ServiceMask
              2 : ServiceState
              3 : NumBindings
              4 : Binding1 Length
              5 : Binding1
              6 :     ..
              7 :     ..
              8 :     ..
              9 :  Binding2 Length
              10 : Binding2
              11 : ..
              12 : ..

Arguments:

    ServiceInfo : pointer to a service info structure.

Return Value:

    NONE.

--*/
{
    DWORD Size;

    //
    // initialize the object elements.
    //

    InitializeCriticalSection( &_ServiceObjCritSect );

    _ServiceInfoLength = 0;
    _ServiceInfoBuffer = NULL;
    _AllottedBufferSize = 0;

    _ServiceState = NULL;
    _ServiceComment = NULL;
    _ServiceMask = NULL;

    TcpsvcsDbgAssert( ServiceInfo != NULL );

    if( ServiceInfo == NULL ) {
        _Status = ERROR_INVALID_PARAMETER;
        return;
    }

    //
    // compute the embedded buffer length.
    //

    Size = ComputeServiceInfoSize( ServiceInfo );
    TcpsvcsDbgAssert( Size != 0 );

    //
    // allocate memory.
    //

    _ServiceInfoBuffer = (LPBYTE) SvclocHeap->Alloc( Size );

    if( _ServiceInfoBuffer == NULL ) {
        _Status = ERROR_NOT_ENOUGH_MEMORY;
        return;
    }

    _ServiceInfoLength = Size;
    _AllottedBufferSize = Size;

    //
    // copy service info.
    //

    CopyServiceInfo( ServiceInfo );

    _Status = ERROR_SUCCESS;
    return;
}


EMBED_SERVICE_INFO::EMBED_SERVICE_INFO(
    LPBYTE InfoBuffer,
    DWORD InfoBufferLength
    )
/*++

Routine Description:

    This function constructs an embedded service info object from a given
        embedded buffer.

Arguments:

    InfoBuffer : pointer to a embedded buffer.

    InfoBufferLength : length of the embedded buffer.

Return Value:

    NONE.

--*/
{
    TcpsvcsDbgAssert( InfoBuffer != NULL );
    TcpsvcsDbgAssert( InfoBufferLength != 0 );

    InitializeCriticalSection( &_ServiceObjCritSect );
    _ServiceInfoLength = InfoBufferLength;
    _ServiceInfoBuffer = InfoBuffer;
    _AllottedBufferSize = 0;

    _ServiceMask = (ULONGLONG *)InfoBuffer;
    _ServiceState = (INET_SERVICE_STATE *)(InfoBuffer + sizeof(ULONGLONG));
    _ServiceComment = (LPSTR)
        (InfoBuffer +
            sizeof(ULONGLONG)
                sizeof(INET_SERVICE_STATE) );

    _Status = ERROR_SUCCESS;
}

EMBED_SERVICE_INFO::~EMBED_SERVICE_INFO(
    VOID
    )
/*++

Routine Description:

    This function destructs a embedded service info object.

Arguments:

    NONE.

Return Value:

    NONE.

--*/
{
    if( _AllottedBufferSize != 0 ) {

        TcpsvcsDbgAssert( _ServiceInfoBuffer != NULL );

        SvclocHeap->Free( _ServiceInfoBuffer );
    }

#if DBG

    _ServiceInfoLength = 0;
    _ServiceInfoBuffer = NULL;
    _AllottedBufferSize = 0;

    _ServiceState = NULL;
    _ServiceComment = NULL;
    _ServiceMask = NULL;

#endif // DBG

    DeleteCriticalSection( &_ServiceObjCritSect );
    _Status = ERROR_SUCCESS;
    return;
}

DWORD
EMBED_SERVICE_INFO::SetServiceInfo(
    LPINET_SERVICE_INFO ServiceInfo
    )
/*++

Routine Description:

    This member function sets the new service info to the embedded service
    info buffer.

Arguments:

    ServiceInfo : pointer to a service info structure.

Return Value:

    Windows error code.

--*/
{
    DWORD Size;

    TcpsvcsDbgAssert( ServiceInfo != NULL );

    LockServiceObj();

    if( ServiceInfo == NULL ) {
        UnlockServiceObj();
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // compute the size of the new service info buffer.
    //

    Size = ComputeServiceInfoSize( ServiceInfo ) ;

    TcpsvcsDbgAssert( Size != 0 );

    if( Size > _AllottedBufferSize ) {

        LPBYTE NewServiceInfoBuffer;

        //
        // free the old buffer and reallocate a new one.
        //

        NewServiceInfoBuffer = (LPBYTE) SvclocHeap->Alloc( Size );

        if( NewServiceInfoBuffer == NULL ) {
            UnlockServiceObj();
            return( ERROR_NOT_ENOUGH_MEMORY );
        }

        SvclocHeap->Free( _ServiceInfoBuffer );
        _ServiceInfoBuffer = NewServiceInfoBuffer;

        _AllottedBufferSize = Size;
    }

    _ServiceInfoLength = Size;

    //
    // now copy buffer.
    //

    CopyServiceInfo( ServiceInfo );

    UnlockServiceObj();
    return( ERROR_SUCCESS );
}

DWORD
EMBED_SERVICE_INFO::GetServiceInfo(
    LPINET_SERVICE_INFO *ServiceInfo
    )
/*++

Routine Description:

    This member function allocates memory for service info structure and
    copies service info from embedded service info buffer.

Arguments:

    ServiceInfo : pointer to a location where the server info structure
        pointer is returned. The caller should free the structure after
        use.

Return Value:

    Windows error code.

--*/
{
    DWORD Error;
    LPBYTE BufferPtr = _ServiceInfoBuffer;
    LPBYTE EndBufferPtr = _ServiceInfoBuffer + _ServiceInfoLength;
    DWORD NumBindings;
    DWORD i;

    LPINET_SERVICE_INFO LocalServiceInfo = NULL;

    LockServiceObj();

    //
    // allocate memory for the service info structure.
    //

    LocalServiceInfo = (LPINET_SERVICE_INFO)
        SvclocHeap->Alloc( sizeof( INET_SERVICE_INFO ) );

    if( LocalServiceInfo == NULL ) {
        Error = ERROR_NOT_ENOUGH_MEMORY;
        goto Cleanup;
    }

    //
    // copy main structure first.
    //

    LocalServiceInfo->ServiceMask = *(ULONGLONG *)BufferPtr;
    BufferPtr += sizeof(ULONGLONG);

    LocalServiceInfo->ServiceState = *(INET_SERVICE_STATE *)BufferPtr;
    BufferPtr += sizeof(INET_SERVICE_STATE);

    //
    // allocate memory for the service comment.
    //

    DWORD CommentLen;

    CommentLen =
        ROUND_UP_COUNT(
            (strlen((LPSTR)BufferPtr) + 1) * sizeof(CHAR),
                ALIGN_DWORD );

     LocalServiceInfo->ServiceComment = (LPSTR)
         SvclocHeap->Alloc( CommentLen );

     if( LocalServiceInfo->ServiceComment == NULL ) {
         Error = ERROR_NOT_ENOUGH_MEMORY;
         goto Cleanup;
     }

     //
     // copy service comment.
     //

     strcpy(
         LocalServiceInfo->ServiceComment,
         (LPSTR)BufferPtr );

    BufferPtr += CommentLen;

    NumBindings = *(DWORD *)BufferPtr;
    BufferPtr += sizeof(DWORD);

    LocalServiceInfo->Bindings.NumBindings = 0;
    LocalServiceInfo->Bindings.BindingsInfo =  NULL;

    if( NumBindings != 0 ) {

        LPINET_BIND_INFO Bindings;

        //
        // allocate memory for bindingsinfo array.
        //

        Bindings = (LPINET_BIND_INFO)
            SvclocHeap->Alloc( sizeof( INET_BIND_INFO )  * NumBindings );

        if( Bindings == NULL ) {
            Error = ERROR_NOT_ENOUGH_MEMORY;
            goto Cleanup;
        }

        LocalServiceInfo->Bindings.BindingsInfo = Bindings;

        for( i = 0; i < NumBindings; i++ ) {

            LPBYTE BindData;

            TcpsvcsDbgAssert( BufferPtr < EndBufferPtr );

            Bindings[i].Length = *(DWORD *)BufferPtr;
            BufferPtr += sizeof(DWORD);

            //
            // allocate memory for the bind data.
            //

            BindData = (LPBYTE)SvclocHeap->Alloc( Bindings[i].Length );

            if( BindData == NULL ) {

                //
                // free the bindings structure memory only if NumBindings
                // is zero, otherwise it will be freed later on along
                // with some other memory blocks.
                //

                if( LocalServiceInfo->Bindings.NumBindings == 0 ) {

                    if( LocalServiceInfo->Bindings.BindingsInfo != NULL ) {
                        SvclocHeap->Free( LocalServiceInfo->Bindings.BindingsInfo );
                        LocalServiceInfo->Bindings.BindingsInfo = NULL;
                    }
                }

                Error = ERROR_NOT_ENOUGH_MEMORY;
                goto Cleanup;
            }

            //
            // copy bind data.
            //

            memcpy( BindData, BufferPtr, Bindings[i].Length );
            BufferPtr += ROUND_UP_COUNT( Bindings[i].Length, ALIGN_DWORD );

            //
            // successfully copied one more bind data.
            //

            Bindings[i].BindData = BindData;
            LocalServiceInfo->Bindings.NumBindings++;
        }
    }

    //
    // all done.
    //

    *ServiceInfo = LocalServiceInfo;
    LocalServiceInfo = NULL;
    Error = ERROR_SUCCESS;

Cleanup:

    if( LocalServiceInfo != NULL ) {
        FreeServiceInfo( LocalServiceInfo );
    }

    UnlockServiceObj();
    return( Error );
}

/*---------------------------------------------------------------------*/

BOOL
EMBED_SERVER_INFO::IsServiceEntryExist(
    ULONGLONG ServiceMask,
    LPEMBED_SERVICE_ENTRY *ServiceEntry
    )
/*++

Routine Description:

    This private member function looks up a service entry in the service
    list.

Arguments:

    ServiceMask : mask of the service to look at.

    ServiceEntry : pointer to location where the service entry pointer is
        returned if found.

Return Value:

    TRUE : if the service entry is found in the service list.
    FALSE : otherwise.

--*/
{

    PLIST_ENTRY SList;
    LPEMBED_SERVICE_ENTRY SEntry;
    ULONGLONG SMask;

    //
    // Scan service list.
    //

    for( SList = _ServicesList.Flink; SList != &_ServicesList; SList = SList->Flink ) {

        SEntry = (LPEMBED_SERVICE_ENTRY)SList;

        //
        // Get Service Mask.
        //

        SMask =  (SEntry->ServiceObject)->GetServiceMask();

        if( SMask == ServiceMask ) {

            //
            // found the service entry.
            //

            *ServiceEntry = SEntry;
            return( TRUE );
        }
    }

    return( FALSE );
}

EMBED_SERVER_INFO::EMBED_SERVER_INFO(
    WORD MajorVersion,
    WORD MinorVersion,
    LPSTR ServerName
    )
/*++

Routine Description:

    This member function constructs a server info object.

Arguments:

    MajorVersion :  major version number of the server software.

    MinorVersion :  minor version number of the server software.

    ServerName : computer name of the server.

Return Value:

    None.

--*/
{
    DWORD Size;
    LPBYTE BufferPtr;
    DWORD ServerNameLen;

    //
    // init object fields.
    //

    InitializeCriticalSection( &_ServerObjCritSect );

    _ServerInfoLength = 0;
    _ServerInfoBuffer = NULL;
    _AllottedBufferSize = 0;

    _VersionNum = NULL;
    _ServerLoad = NULL;
    _ServicesMask = NULL;
    _ServerName = NULL;
    _NumServices = NULL;

    InitializeListHead( &_ServicesList );

    //
    // compute Server Info Size.
    //

    ServerNameLen = ROUND_UP_COUNT(
                        (strlen(ServerName) + 1) * sizeof(CHAR),
                            ALIGN_DWORD);

    Size = sizeof(INET_VERSION_NUM) +   // for version number
                    ServerNameLen +     // for server name
                    sizeof(DWORD) +     // for load factor
                    sizeof(ULONGLONG) +  // for services mask.
                    sizeof(DWORD);      // for number of services.

    _ServerInfoBuffer = (LPBYTE)SvclocHeap->Alloc( Size );

    if( _ServerInfoBuffer == NULL ) {
        _Status = ERROR_NOT_ENOUGH_MEMORY;
        return;
    }

    _ServerInfoLength = Size;
    _AllottedBufferSize = Size;

    BufferPtr = _ServerInfoBuffer;

    ((INET_VERSION_NUM *)BufferPtr)->Version.Major = MajorVersion;
    ((INET_VERSION_NUM *)BufferPtr)->Version.Minor = MinorVersion;

    _VersionNum = (INET_VERSION_NUM *)BufferPtr;
    BufferPtr += sizeof(INET_VERSION_NUM);

   strcpy( (LPSTR)BufferPtr, ServerName );
    _ServerName = (LPSTR)BufferPtr;
    BufferPtr += ServerNameLen;

    *(DWORD *)BufferPtr  = 0; // load factor
    _ServerLoad = (DWORD *)BufferPtr;
    BufferPtr += sizeof(DWORD);

    *(ULONGLONG *)BufferPtr  = 0; // services mask;
    _ServicesMask = (ULONGLONG *)BufferPtr;
    BufferPtr += sizeof(ULONGLONG);

    *(DWORD *)BufferPtr  = 0; // num services.
    _NumServices = (DWORD *)BufferPtr;
    BufferPtr += sizeof(DWORD);

    TcpsvcsDbgAssert( BufferPtr == (_ServerInfoBuffer + _ServerInfoLength) );

    _Status = ERROR_SUCCESS;
    return;
}

EMBED_SERVER_INFO::EMBED_SERVER_INFO(
    LPBYTE ResponseBuffer,
    DWORD ResponseBufferLength
    )
/*++

Routine Description:

    This member function constructs a server info object from embedded
    server info buffer (received from the server).

Arguments:

    ResponseBuffer : pointer to the embedded server info buffer.

    ResponseBufferLength : length of the above buffer.

Return Value:

--*/
{
    LPBYTE BufferPtr;
    DWORD ServerNameLen;
    DWORD nServices;
    DWORD i;

    InitializeCriticalSection( &_ServerObjCritSect );

    //
    // set object fields.
    //

    BufferPtr = _ServerInfoBuffer = ResponseBuffer;

    _VersionNum = (INET_VERSION_NUM *)BufferPtr;
    BufferPtr += sizeof(INET_VERSION_NUM); // skip version number.

    _ServerName = (LPSTR)BufferPtr;
    ServerNameLen =
        ROUND_UP_COUNT(
                (strlen((LPSTR)BufferPtr) + 1) * sizeof(CHAR),
                ALIGN_DWORD);

    BufferPtr += ServerNameLen;

    _ServerLoad = (DWORD *)BufferPtr;
    BufferPtr += sizeof(DWORD);

    _ServicesMask = (ULONGLONG *)BufferPtr;
    BufferPtr += sizeof(ULONGLONG);

    _NumServices = (DWORD *)BufferPtr;
    BufferPtr += sizeof(DWORD);

    _ServerInfoLength = BufferPtr - _ServerInfoBuffer;
    _AllottedBufferSize = 0;

    nServices = *_NumServices;

    InitializeListHead( &_ServicesList );

    //
    // now make service objects.
    //

    for( i = 0; i < nServices; i++) {

        DWORD ServiceBufferLength;
        LPSERVICE_OBJECT ServiceObject;
        DWORD ObjStatus;
        LPEMBED_SERVICE_ENTRY ServiceEntry;

        ServiceBufferLength = *BufferPtr;
        BufferPtr += sizeof(DWORD);

        //
        // make another service object.
        //

        ServiceObject =
            new EMBED_SERVICE_INFO( BufferPtr, ServiceBufferLength );

        if( ServiceObject == NULL ) {
            _Status = ERROR_NOT_ENOUGH_MEMORY;
            return;
        }

        ObjStatus = ServiceObject->GetStatus();

        if( ObjStatus != ERROR_SUCCESS ) {
            _Status = ObjStatus;
            delete ServiceObject;
            return;
        }

        //
        // allocate space for a new service entry.
        //

        ServiceEntry = (LPEMBED_SERVICE_ENTRY)
            SvclocHeap->Alloc( sizeof(EMBED_SERVICE_ENTRY) );

        if( ServiceEntry == NULL ) {
            _Status = ERROR_NOT_ENOUGH_MEMORY;
            delete ServiceObject;
            return;
        }

        ServiceEntry->ServiceObject = ServiceObject;

        //
        // add this new entry to the list.
        //

        InsertTailList( &_ServicesList, &ServiceEntry->NextEntry );

        //
        // point to the next service record.
        //

        BufferPtr += ServiceBufferLength;
    }

    _Status = ERROR_SUCCESS;
    return;
}

EMBED_SERVER_INFO::~EMBED_SERVER_INFO(
    VOID
    )
/*++

Routine Description:

    This member function destructs a server info object.

Arguments:

    NONE.

Return Value:

    NONE.

--*/
{
    //
    // delete all service objects first.
    //

    while( !IsListEmpty( &_ServicesList ) ) {

        LPEMBED_SERVICE_ENTRY ServiceEntry;

        //
        // remove an entry from the tail of the list.
        //

        ServiceEntry =
            (LPEMBED_SERVICE_ENTRY)RemoveTailList( &_ServicesList );

        //
        // delete service object.
        //

        delete ServiceEntry->ServiceObject;

        //
        // free the entry memory.
        //

        SvclocHeap->Free( ServiceEntry );
    }

    //
    // free up server info buffer.
    //

    if( _AllottedBufferSize != 0 ) {

        TcpsvcsDbgAssert( _ServerInfoBuffer != NULL );
        SvclocHeap->Free( _ServerInfoBuffer );
    }


#if DBG

    _ServerInfoLength = 0;
    _ServerInfoBuffer = NULL;
    _AllottedBufferSize = 0;

    _VersionNum = NULL;
    _ServerLoad = NULL;
    _ServicesMask = NULL;
    _ServerName = NULL;

    _NumServices = NULL;

#endif // DBG

    DeleteCriticalSection( &_ServerObjCritSect );

    _Status = ERROR_SUCCESS;
    return;
}

DWORD
EMBED_SERVER_INFO::AddService (
    LPINET_SERVICE_INFO ServiceInfo
    )
/*++

Routine Description:

    This member function adds or replaces a service info object to the
    server info object.

Arguments:

    ServiceInfo : pointer to the service info structure.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    LPSERVICE_OBJECT ServiceObj;
    LPEMBED_SERVICE_ENTRY ServiceEntry = NULL;
    ULONGLONG SMask;

    LockServerObj();

    SMask = ServiceInfo->ServiceMask;

    if( IsServiceEntryExist( SMask, &ServiceEntry ) ) {

        //
        // this service already exists, so just update the content.
        //

        TcpsvcsDbgAssert( ServiceEntry != NULL );
        TcpsvcsDbgAssert( (*_ServicesMask & SMask) == SMask );

        //
        // set service info.
        //

        Error = (ServiceEntry->ServiceObject)->SetServiceInfo( ServiceInfo );

        UnlockServerObj();
        return( Error );
    }

    //
    // new entry.
    //

    TcpsvcsDbgAssert( (*_ServicesMask & SMask)  == 0 );

    //
    // make a service object.
    //

    ServiceObj = new EMBED_SERVICE_INFO( ServiceInfo );

    if( ServiceObj == NULL ) {
        UnlockServerObj();
        return( ERROR_NOT_ENOUGH_MEMORY );
    }

    Error = ServiceObj->GetStatus();

    if( Error != ERROR_SUCCESS ) {
        delete ServiceObj;
        UnlockServerObj();
        return( Error );
    }

    //
    // allocate memory for the new service entry.
    //

    ServiceEntry = (LPEMBED_SERVICE_ENTRY)
        SvclocHeap->Alloc( sizeof(EMBED_SERVICE_ENTRY) );

    if( ServiceEntry == NULL ) {
        delete ServiceObj;
        UnlockServerObj();
        return( ERROR_NOT_ENOUGH_MEMORY );
    }

    ServiceEntry->ServiceObject = ServiceObj;

    //
    // Adjust parameters.
    //

    *_ServicesMask |= SMask;
    (*_NumServices)++;

    //
    // add this entry to the service list.
    //

    InsertTailList(&_ServicesList, &ServiceEntry->NextEntry);

    UnlockServerObj();
    return( ERROR_SUCCESS );
}

DWORD
EMBED_SERVER_INFO::RemoveService(
    ULONGLONG SMask
    )
/*++

Routine Description:

    This member function removes a service info object from the server
    info object.

Arguments:

    SMask : Service mask of the service to be removed from the
        server object.

Return Value:

    Windows Error Code.

--*/
{

    LPEMBED_SERVICE_ENTRY ServiceEntry = NULL;

    LockServerObj();

    //
    // check the service is in the service list.
    //

    if( IsServiceEntryExist( SMask, &ServiceEntry )  == FALSE ) {

        TcpsvcsDbgAssert( (*_ServicesMask &  SMask) == 0);
        UnlockServerObj();
        return( ERROR_SERVICE_NOT_FOUND );
    }

    TcpsvcsDbgAssert( ServiceEntry != NULL );
    TcpsvcsDbgAssert( *_ServicesMask & SMask );

    //
    // adjust parameters.
    //

    *_ServicesMask &= ~SMask;
    *_NumServices--;

    //
    // remove entry from list.
    //

    RemoveEntryList( &ServiceEntry->NextEntry );

    //
    // delete service object.
    //

    delete ServiceEntry->ServiceObject;

    //
    // free entry memory.
    //

    SvclocHeap->Free( ServiceEntry );

    UnlockServerObj();
    return( ERROR_SUCCESS );
}

DWORD
EMBED_SERVER_INFO::SetServiceState(
    ULONGLONG SMask,
    INET_SERVICE_STATE ServiceState
    )
/*++

Routine Description:

    This member function sets the state of a service.

Arguments:

    SMask : Service Mask of the service whose state to be set.

    ServiceState : New state of the service.

Return Value:

    None.

--*/
{
    LPEMBED_SERVICE_ENTRY ServiceEntry = NULL;

    LockServerObj();
    //
    // check the service is in the service list.
    //

    if( IsServiceEntryExist( SMask, &ServiceEntry )  == FALSE ) {

        TcpsvcsDbgAssert( (*_ServicesMask &  SMask) == 0);
        UnlockServerObj();
        return( ERROR_SERVICE_NOT_FOUND );
    }

    TcpsvcsDbgAssert( ServiceEntry != NULL );
    TcpsvcsDbgAssert( *_ServicesMask & SMask );

    //
    // set service state.
    //

    (ServiceEntry->ServiceObject)->SetServiceState( ServiceState );

    UnlockServerObj();
    return( ERROR_SUCCESS );
}

DWORD
EMBED_SERVER_INFO::ComputeResponseLength(
    VOID
    )
/*++

Routine Description:

    This member function computes the length of the response message
    (containing server info and all services info in the embedded
    formatted)  sent to a client.

Arguments:

    None.

Return Value:

    Length of the response.

--*/
{
    DWORD Size = 0;
    PLIST_ENTRY SList;
    LPEMBED_SERVICE_ENTRY SEntry;

    LockServerObj();
    //
    // Compute response length of  the services.
    //

    for( SList = _ServicesList.Flink; SList != &_ServicesList; SList = SList->Flink ) {

        SEntry = (LPEMBED_SERVICE_ENTRY)SList;

        //
        // Get Service info buffer size.
        //

        Size +=  (SEntry->ServiceObject)->GetServiceInfoLength();
        Size += sizeof(DWORD); // for service length info itself.
    }

    //
    // server info size.
    //

    Size += _ServerInfoLength;

    UnlockServerObj();
    return( Size );
}

DWORD
EMBED_SERVER_INFO::MakeResponseMessage(
    LPBYTE MessageBuffer,
    DWORD BufferLength
    )
/*++

Routine Description:

    This member function builds a response message sent to a client.

Arguments:

    MessageBuffer : pointer to a buffer where the response message is
        built.

    BufferLength : length of the message.

Return Value:

    Windows Error Code.

--*/
{
    LPBYTE BufferPtr = MessageBuffer;
    LPBYTE EndBufferPtr = MessageBuffer + BufferLength;
    LPEMBED_SERVICE_ENTRY SEntry;
    ULONGLONG SMask;
    DWORD Error;
    DWORD RequiredBufferLength;
    PLIST_ENTRY SList;

    LockServerObj();

    RequiredBufferLength = ComputeResponseLength();

    if( RequiredBufferLength > BufferLength ) {
        UnlockServerObj();
        return( ERROR_INSUFFICIENT_BUFFER );
    }

    //
    // copy server info first.
    //

    memcpy( BufferPtr, _ServerInfoBuffer, _ServerInfoLength );
    BufferPtr += _ServerInfoLength;

    //
    // copy all service info buffers.
    //

    for( SList = _ServicesList.Flink; SList != &_ServicesList; SList = SList->Flink ) {

        DWORD *_ServiceInfoLength;

        TcpsvcsDbgAssert( BufferPtr < EndBufferPtr );

        _ServiceInfoLength = (DWORD *)BufferPtr;
        BufferPtr += sizeof(DWORD);

        SEntry = (LPEMBED_SERVICE_ENTRY)SList;

        //
        // Get Service Mask.
        //

        Error = (SEntry->ServiceObject)->GetServiceInfoBuffer(
                        BufferPtr,
                        (EndBufferPtr - BufferPtr),
                        _ServiceInfoLength );

        if( Error != ERROR_SUCCESS ) {
            UnlockServerObj();
            return( Error );
        }

        BufferPtr += *_ServiceInfoLength;
    }

    UnlockServerObj();
    return( ERROR_SUCCESS );
}

DWORD
EMBED_SERVER_INFO::GetServerInfo(
    LPINET_SERVER_INFO *ServerInfo
    )
/*++

Routine Description:

    This member function retrieves the server info structure.

Arguments:

    ServerInfo : pointer to a location where the pointer to the server
        info structure is returned. The member function allots memory for
        the structure, the caller should free the mmeory after use.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    LPINET_SERVER_INFO LocalServerInfo = NULL;
    LPINET_SERVICE_INFO *ServicesInfoArray = NULL;
    PLIST_ENTRY SList;
    DWORD i;

    LockServerObj();

    //
    // allocate memory for the server info structure.
    //

    LocalServerInfo = (LPINET_SERVER_INFO)SvclocHeap->Alloc(sizeof(INET_SERVER_INFO) );

    if( LocalServerInfo == NULL ) {
        Error = ERROR_NOT_ENOUGH_MEMORY;
        goto Cleanup;
    }

    //
    // initialize all fields.
    //

    memset( LocalServerInfo, 0x0, sizeof(INET_SERVER_INFO) );

    //
    // fill in the fields.
    //

    //
    // server info field is fill by some one else !!
    // leave it empty for now.
    //

    LocalServerInfo->ServerAddress.Length = 0;
    LocalServerInfo->ServerAddress.BindData = NULL;

    LocalServerInfo->VersionNum = *_VersionNum;

    //
    // alloc memory for the server name.
    //

    LocalServerInfo->ServerName = (LPSTR)
        SvclocHeap->Alloc( (strlen(_ServerName) + 1) * sizeof(CHAR) );

    if( LocalServerInfo->ServerName == NULL ) {
        Error = ERROR_NOT_ENOUGH_MEMORY;
        goto Cleanup;
    }

    strcpy( LocalServerInfo->ServerName, _ServerName );
    LocalServerInfo->LoadFactor = *_ServerLoad;
    LocalServerInfo->ServicesMask = *_ServicesMask;
    LocalServerInfo->Services.NumServices = 0;
    LocalServerInfo->Services.Services = NULL;

    //
    // allocate memory for the service struct. array pointers.
    //

    ServicesInfoArray = (LPINET_SERVICE_INFO *)
        SvclocHeap->Alloc( (*_NumServices) * sizeof(LPINET_SERVICE_INFO ) );

    if(ServicesInfoArray == NULL ) {
        Error = ERROR_NOT_ENOUGH_MEMORY;
        goto Cleanup;
    }

    memset( ServicesInfoArray, 0x0, (*_NumServices) * sizeof(LPINET_SERVICE_INFO) );

    //
    // now get services info.
    //

    for ( SList = _ServicesList.Flink, i = 0;
            (SList != &_ServicesList) && (i < *_NumServices);
                SList = SList->Flink, i++ ) {

        LPSERVICE_OBJECT SObj;

        SObj = ((LPEMBED_SERVICE_ENTRY)SList)->ServiceObject;

        Error = SObj->GetServiceInfo( &ServicesInfoArray[i] );

        if( Error != ERROR_SUCCESS ) {
            goto Cleanup;
        }
    }

    TcpsvcsDbgAssert( i <= (*_NumServices) );

    LocalServerInfo->Services.NumServices = i;
    LocalServerInfo->Services.Services = ServicesInfoArray;
    ServicesInfoArray = NULL;

    *ServerInfo = LocalServerInfo;
    LocalServerInfo = NULL;

    Error = ERROR_SUCCESS;

Cleanup:

    if( Error != ERROR_SUCCESS ) {

        //
        // Cleanup allotted data.
        //

        if( ServicesInfoArray != NULL ) {

            for ( i = 0; i < (*_NumServices) && ServicesInfoArray[i] != NULL; i++) {
                FreeServiceInfo( ServicesInfoArray[i] );
            }

            SvclocHeap->Free( ServicesInfoArray );
        }

        if( LocalServerInfo != NULL ) {

            if( LocalServerInfo->ServerName != NULL ) {

                SvclocHeap->Free( LocalServerInfo->ServerName );
            }

            SvclocHeap->Free( LocalServerInfo );
        }
    }

    UnlockServerObj();
    return( Error );
}