ANSC_STATUS
BbhmDiagnsRemove
    (
        ANSC_HANDLE                 hThisObject
    )
{
    PBBHM_DIAG_NS_LOOKUP_OBJECT       pMyObject           = (PBBHM_DIAG_NS_LOOKUP_OBJECT)hThisObject;
    PBBHM_NS_LOOKUP_TDO_OBJECT        pStateTimer         = (PBBHM_NS_LOOKUP_TDO_OBJECT )pMyObject->hStateTimer;                        
    

    if ( pStateTimer )
    {
        pStateTimer->Remove((ANSC_HANDLE)pStateTimer);
    }

    pMyObject->Close((ANSC_HANDLE)pMyObject);

    if ( pMyObject->hSendBuffer )
    {
        AnscFreeMemory(pMyObject->hSendBuffer);

        pMyObject->hSendBuffer = NULL;
    }

    pMyObject->Cancel((ANSC_HANDLE)pMyObject);
    pMyObject->Reset ((ANSC_HANDLE)pMyObject);

    AnscFreeLock (&pMyObject->EchoTableLock);
    AnscFreeLock (&pMyObject->PqueryTableLock);

    AnscCoRemove(pMyObject);
    BbhmDiageoRemove((ANSC_HANDLE)pMyObject);

    return  ANSC_STATUS_SUCCESS;
}
ANSC_STATUS
BbhmDiagnsStopDiag
    (
        ANSC_HANDLE                 hThisObject
    )
{
    ANSC_STATUS                     returnStatus        = ANSC_STATUS_SUCCESS;
    PBBHM_DIAG_NS_LOOKUP_OBJECT     pMyObject           = (PBBHM_DIAG_NS_LOOKUP_OBJECT  )hThisObject;

    if ( !(pMyObject->CheckCanStart((ANSC_HANDLE)pMyObject)))
    {
        returnStatus = pMyObject->SetControl((ANSC_HANDLE)pMyObject, BBHM_NS_LOOKUP_CONTROL_STOP);

        return  returnStatus;
    }
    return  ANSC_STATUS_SUCCESS;
}
ANSC_STATUS
BbhmDiagnsRecv
    (
        ANSC_HANDLE                 hThisObject,
        ANSC_HANDLE                 hXsinkObject,
        PVOID                       buffer,
        ULONG                       ulSize
    )
{
    ANSC_STATUS                     returnStatus = ANSC_STATUS_SUCCESS;
    PBBHM_DIAG_NS_LOOKUP_OBJECT     pMyObject    = (PBBHM_DIAG_NS_LOOKUP_OBJECT )hThisObject;
    PBBHM_NS_LOOKUP_PROPERTY        pProperty    = (PBBHM_NS_LOOKUP_PROPERTY    )&pMyObject->Property;
    PBBHM_NS_LOOKUP_TDO_OBJECT      pStateTimer  = (PBBHM_NS_LOOKUP_TDO_OBJECT  )pMyObject->hStateTimer;
    PBBHM_NS_LOOKUP_XSINK_OBJECT    pXsink       = (PBBHM_NS_LOOKUP_XSINK_OBJECT)hXsinkObject;
    PANSC_XSOCKET_OBJECT            pXsocket     = (PANSC_XSOCKET_OBJECT        )pXsink->GetXsocket((ANSC_HANDLE)pXsink);
    PDSLH_NSLOOKUP_INFO             pDiagInfo    = (PDSLH_NSLOOKUP_INFO         )pMyObject->hDslhDiagInfo;
    PDNS_HEADER                     pDnsHeader   = (PDNS_HEADER)buffer;
    PBBHM_NS_LOOKUP_ECHO_ENTRY      pEchoEntry   = NULL;
    PBBHM_NS_LOOKUP_QUERY_ENTRY     pQuery       = NULL;
    ULONG                           StopTime     = 0;
    PSINGLE_LINK_ENTRY              pSLinkEntry  = NULL;
    char*                           p            = NULL;

    StopTime = AnscGetTickInMilliSeconds();


    if ( pProperty->Status != BBHM_NS_LOOKUP_STATUS_RUNNING )
    {
        return  ANSC_STATUS_UNAPPLICABLE;
    }

    /* Temporarily disable this check since we will check pQuery later */
/*
    if ( pXsocket->PeerAddress.Value != pProperty->DstIp.Value )
    {
        return  ANSC_STATUS_FAILURE;
    }
*/

    pQuery = pMyObject->GetPqueryById(pMyObject, AnscDnsGetId(pDnsHeader));
    if ( pQuery )
    {
        if ( AnscDnsGetRcode(pDnsHeader) != 0 )
        {
            if ( AnscDnsGetRcode(pDnsHeader) == DNS_RCODE_NAME_ERROR )
            {
                p = pDiagInfo->HostName + AnscSizeOfString(pDiagInfo->HostName) + 1;

                if ( AnscSizeOfString(p) )
                {
                    if ( !AnscEqualString(p - AnscSizeOfString(p) - 1, p, TRUE) )
                    {
                        pStateTimer->Stop((ANSC_HANDLE)pStateTimer);
                        pProperty->Control = BBHM_NS_LOOKUP_CONTROL_STOP;
                        pMyObject->DelAllPqueries(hThisObject);
                        pMyObject->PopEchoEntry(hThisObject);
                        pMyObject->ResetPropertyCounter(hThisObject);

                        *(p - 1) = '.';
                        pMyObject->SetControl   ((ANSC_HANDLE)pMyObject, BBHM_NS_LOOKUP_CONTROL_START);
                        BbhmDiageoStartDiag     ((ANSC_HANDLE)pMyObject);
                        return  ANSC_STATUS_SUCCESS;
                    }
                }
            }


            pSLinkEntry = AnscSListGetFirstEntry(&pMyObject->EchoTable);

            while ( pSLinkEntry )
            {
                pEchoEntry  = ACCESS_BBHM_NS_LOOKUP_ECHO_ENTRY(pSLinkEntry);
                pSLinkEntry = AnscSListGetNextEntry(pSLinkEntry);

                if ( pQuery->QueryId == pEchoEntry->QueryId )
                {
                    break;
                }
            }

            if ( AnscDnsGetRcode(pDnsHeader) == DNS_RCODE_NAME_ERROR )
            {
                pEchoEntry->Status = BBHM_NS_LOOKUP_STATUS_Error_HostNameNotResolved;
            }
            else if ( AnscDnsGetRcode(pDnsHeader) == DNS_RCODE_SERVER_FAILURE ||  AnscDnsGetRcode(pDnsHeader) == DNS_RCODE_REFUSED )
            {
                pEchoEntry->Status = BBHM_NS_LOOKUP_STATUS_Error_DNSServerNotAvailable;
            }
            else
            {
                pEchoEntry->Status = BBHM_NS_LOOKUP_STATUS_Error_Other;
            }
            pEchoEntry->AnswerType = AnscDnsIsAuthoritativeAnswer(pDnsHeader) ? BBHM_NS_LOOKUP_RESULT_Authoritative : BBHM_NS_LOOKUP_RESULT_NonAuthoritative;
            pEchoEntry->HostNameReturned = NULL;
            pEchoEntry->IPAddresses = NULL;
            pMyObject->DelPquery(pMyObject, pQuery);

            pProperty->PktsRecv++;
        }
        else
        {
            returnStatus = pMyObject->SetStopTime((ANSC_HANDLE)pMyObject, pQuery, pDnsHeader, StopTime);

            if ( returnStatus == ANSC_STATUS_SUCCESS )
            {
                pProperty->PktsRecv++;
            }
        }

        if ( pProperty->PktsRecv == pProperty->NumPkts )
        {
            pMyObject->SetStatus((ANSC_HANDLE)pMyObject, BBHM_NS_LOOKUP_STATUS_COMPLETE);
            pMyObject->Stop((ANSC_HANDLE)pMyObject);
        }

        return  returnStatus;
    }

    return  ANSC_STATUS_SUCCESS;
}
ANSC_STATUS
BbhmDiagnsStartDiag
    (
        ANSC_HANDLE                 hThisObject
    )
{
    ANSC_STATUS                     returnStatus        = ANSC_STATUS_SUCCESS;
    PBBHM_DIAG_NS_LOOKUP_OBJECT     pMyObject           = (PBBHM_DIAG_NS_LOOKUP_OBJECT)hThisObject;
    PDSLH_NSLOOKUP_INFO             pDiagInfo           = (PDSLH_NSLOOKUP_INFO)pMyObject->hDslhDiagInfo;
    PBBHM_NS_LOOKUP_PROPERTY        pProperty           = (PBBHM_NS_LOOKUP_PROPERTY   )&pMyObject->Property;

    if ( pDiagInfo->bForced != FALSE )
    {
        if ( !(pMyObject->CheckCanStart((ANSC_HANDLE)pMyObject)))
        {
            returnStatus = pMyObject->SetControl((ANSC_HANDLE)pMyObject, BBHM_NS_LOOKUP_CONTROL_STOP);
        }
        pMyObject->ResetProperty    ((ANSC_HANDLE)pMyObject);

        if ( pMyObject->SetDstIp    ((ANSC_HANDLE)pMyObject, pDiagInfo->DNSServer) == ANSC_STATUS_SUCCESS)
        {
            pMyObject->SetNumPkts   ((ANSC_HANDLE)pMyObject, pDiagInfo->NumberOfRepetitions);
            pMyObject->SetSrcIp     ((ANSC_HANDLE)pMyObject, pDiagInfo->IfAddr );
            pMyObject->SetTimeOut   ((ANSC_HANDLE)pMyObject, pDiagInfo->Timeout);

            pMyObject->Open         (pMyObject);
            pMyObject->SetControl   ((ANSC_HANDLE)pMyObject, BBHM_NS_LOOKUP_CONTROL_START);
            BbhmDiageoStartDiag     ((ANSC_HANDLE)pMyObject);
        }
        else
        {
            pDiagInfo->DiagnosticState = DSLH_DIAG_STATE_TYPE_Error_NSLookup_DNSServer;
        }
        return ANSC_STATUS_SUCCESS;
    }
    else
    {
        if ( pMyObject->CheckCanStart   ((ANSC_HANDLE)pMyObject) )
        {
            pMyObject->ResetProperty    ((ANSC_HANDLE)pMyObject);

            if ( pMyObject->SetDstIp    ((ANSC_HANDLE)pMyObject, pDiagInfo->DNSServer) == ANSC_STATUS_SUCCESS)
            {
                pMyObject->SetNumPkts   ((ANSC_HANDLE)pMyObject, pDiagInfo->NumberOfRepetitions);
                pMyObject->SetSrcIp     ((ANSC_HANDLE)pMyObject, pDiagInfo->IfAddr );
                pMyObject->SetTimeOut   ((ANSC_HANDLE)pMyObject, pDiagInfo->Timeout);

                pMyObject->Open         ((ANSC_HANDLE)pMyObject);
                pMyObject->SetControl   ((ANSC_HANDLE)pMyObject, BBHM_NS_LOOKUP_CONTROL_START);
                BbhmDiageoStartDiag     ((ANSC_HANDLE)pMyObject);
            }
            else
            {
                pDiagInfo->DiagnosticState = DSLH_DIAG_STATE_TYPE_Error_NSLookup_DNSServer;
            }
            return ANSC_STATUS_SUCCESS;
        }
        else
        {
            AnscTraceFlow(("BbhmDiagnsStartDiag -- query task is already running...\n"));

            return  ANSC_STATUS_PENDING;
        }
    }
}
ANSC_STATUS
BbhmDiagnsInitialize
    (
        ANSC_HANDLE                 hThisObject
    )
{
    PBBHM_DIAG_NS_LOOKUP_OBJECT       pMyObject    = (PBBHM_DIAG_NS_LOOKUP_OBJECT)hThisObject;

    /*
     * Until you have to simulate C++ object-oriented programming style with standard C, you don't
     * appreciate all the nice little things come with C++ language and all the dirty works that
     * have been done by the C++ compilers. Member initialization is one of these things. While in
     * C++ you don't have to initialize all the member fields inherited from the base class since
     * the compiler will do it for you, such is not the case with C.
     */
     BbhmDiageoInitialize((ANSC_HANDLE)pMyObject);

    /*
     * Although we have initialized some of the member fields in the "create" member function, we
     * repeat the work here for completeness. While this simulation approach is pretty stupid from
     * a C++/Java programmer perspective, it's the best we can get for universal embedded network
     * programming. Before we develop our own operating system (don't expect that to happen any
     * time soon), this is the way things gonna be.
     */
    pMyObject->Oid                  = BBHM_DIAG_NS_LOOKUP_OID;
    pMyObject->QueryId              = 0; 
    pMyObject->bActive              = FALSE;

    pMyObject->Reset                = BbhmDiagnsReset;
    pMyObject->CopyDiagParams       = BbhmDiagnsCopyDiagParams;

    /* overwrite the virtual functions */
    pMyObject->StartDiag            = BbhmDiagnsStartDiag;
    pMyObject->StopDiag             = BbhmDiagnsStopDiag;
    pMyObject->RetrieveResult       = BbhmDiagnsRetrieveResult;


    pMyObject->ResetProperty        = BbhmDiagnsResetProperty;
    pMyObject->ResetPropertyCounter = BbhmDiagnsResetPropertyCounter;
    pMyObject->Reset                = BbhmDiagnsReset;
                                    
    pMyObject->Start                = BbhmDiagnsStart;
    pMyObject->Stop                 = BbhmDiagnsStop;
    pMyObject->Open                 = BbhmDiagnsOpen;
    pMyObject->Close                = BbhmDiagnsClose;
    pMyObject->Expire1              = BbhmDiagnsExpire1;
    pMyObject->Expire2              = BbhmDiagnsExpire2;
                                    
    pMyObject->SetStopTime          = BbhmDiagnsSetStopTime;
    pMyObject->AddEchoEntry         = BbhmDiagnsAddEchoEntry;
    pMyObject->PopEchoEntry         = BbhmDiagnsPopEchoEntry;

    pMyObject->Recv                 = BbhmDiagnsRecv;
    pMyObject->Send                 = BbhmDiagnsSend;
                                    
    pMyObject->GetSrcIp             = BbhmDiagnsGetSrcIp;       
    pMyObject->SetSrcIp             = BbhmDiagnsSetSrcIp;   
    pMyObject->GetDstIp             = BbhmDiagnsGetDstIp;       
    pMyObject->SetDstIp             = BbhmDiagnsSetDstIp;       
    pMyObject->GetNumPkts           = BbhmDiagnsGetNumPkts;     
    pMyObject->SetNumPkts           = BbhmDiagnsSetNumPkts;         
    pMyObject->GetTimeOut           = BbhmDiagnsGetTimeOut;     
    pMyObject->SetTimeOut           = BbhmDiagnsSetTimeOut;         
    pMyObject->SetControl           = BbhmDiagnsSetControl;             
    pMyObject->SetStatus            = BbhmDiagnsSetStatus;  
    
    pMyObject->AddPquery            = BbhmDiagnsAddPquery;
    pMyObject->GetPqueryById        = BbhmDiagnsGetPqueryById;
    pMyObject->DelPquery            = BbhmDiagnsDelPquery;
    pMyObject->DelAllPqueries       = BbhmDiagnsDelAllPqueries;
    pMyObject->SetDiagParams        = BbhmDiagnsSetDiagParams;
    pMyObject->CalculateResult      = BbhmDiagnsCalculateResult;
    pMyObject->GetStatus            = BbhmDiagnsGetStatus;

    /*
     * We shall initialize the session properties to the default values, which may be changed later
     * via the "configure" member function. If any of the future extensions needs to change the
     * session property, the following code also needs to be changed.
     */
    pMyObject->ResetProperty((ANSC_HANDLE)pMyObject);

    AnscSListInitializeHeader(&pMyObject->EchoTable);
    AnscSListInitializeHeader(&pMyObject->PqueryTable);

    AnscInitializeLock       (&pMyObject->EchoTableLock);
    AnscInitializeLock       (&pMyObject->PqueryTableLock);

    return  ANSC_STATUS_SUCCESS;
}