/*++

Copyright (c) 1994  Microsoft Corporation

Module Name:

    dhcp.c

Abstract:

    This module contains DHCP specific utility routines used by the
    DHCP components.

Author:

    Manny Weiser (mannyw) 12-Aug-1992

Revision History:

    Madan Appiah (madana) 21-Oct-1992

--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <dhcpl.h>

DWORD
NTTimeToNTPTime(PULONG pTime,
                PFILETIME pft OPTIONAL);

DWORD
NTPTimeToNTFileTime(PLONG pTime, PFILETIME pft, BOOL bHostOrder);

#undef DhcpAllocateMemory
#undef DhcpFreeMemory


PVOID
DhcpAllocateMemory(
    DWORD Size
    )
/*++

Routine Description:

    This function allocates the required size of memory by calling
    LocalAlloc.

Arguments:

    Size - size of the memory block required.

Return Value:

    Pointer to the allocated block.

--*/
{

    ASSERT( Size != 0 );

    return( LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Size ) );
}

#undef DhcpFreeMemory

VOID
DhcpFreeMemory(
    PVOID Memory
    )
/*++

Routine Description:

    This function frees up the memory that was allocated by
    DhcpAllocateMemory.

Arguments:

    Memory - pointer to the memory block that needs to be freed up.

Return Value:

    none.

--*/
{

    LPVOID Ptr;

    ASSERT( Memory != NULL );
    Ptr = LocalFree( Memory );

    ASSERT( Ptr == NULL );
}



DATE_TIME
DhcpCalculateTime(
    DWORD RelativeTime
    )
/*++

Routine Description:

    The function calculates the absolute time of a time RelativeTime
    seconds from now.

Arguments:

    RelativeTime - Relative time, in seconds.

Return Value:

    The time in RelativeTime seconds from the current system time.

--*/
{
    SYSTEMTIME systemTime;
    ULONGLONG absoluteTime;

    if( RelativeTime == INFINIT_LEASE ) {
        ((DATE_TIME *)&absoluteTime)->dwLowDateTime =
            DHCP_DATE_TIME_INFINIT_LOW;
        ((DATE_TIME *)&absoluteTime)->dwHighDateTime =
            DHCP_DATE_TIME_INFINIT_HIGH;
    }
    else {

        GetSystemTime( &systemTime );
        SystemTimeToFileTime(
            &systemTime,
            (FILETIME *)&absoluteTime );

        absoluteTime = absoluteTime + RelativeTime * (ULONGLONG)10000000; }

    return( *(DATE_TIME *)&absoluteTime );
}


DATE_TIME
DhcpGetDateTime(
    VOID
    )
/*++

Routine Description:

    This function returns FILETIME.

Arguments:

    none.

Return Value:

    FILETIME.

--*/
{
    SYSTEMTIME systemTime;
    DATE_TIME Time;

    GetSystemTime( &systemTime );
    SystemTimeToFileTime( &systemTime, (FILETIME *)&Time );

    return( Time );
}

VOID
DhcpNTToNTPTime(
    LPDATE_TIME AbsNTTime,
    DWORD       Offset,
    PULONG      NTPTimeStamp
    )
/*++

Routine Description:

    The function calculates the absolute NTP timestamp from AbsTime on
    NT added by given offset.

Arguments:

    AbsNTTime - AbsTime on NT. If 0, it will use current time.

    RelativeOffset - offset to be added to AnsNTTime (in seconds.)

Return Value:

    The time in RelativeTime seconds from the current system time.

--*/
{
    ULONGLONG   LocalAbsNTTime;
    DWORD       Error;

    if ( AbsNTTime == 0 ) {
        GetSystemTimeAsFileTime((FILETIME *)&LocalAbsNTTime );
    } else {
        LocalAbsNTTime = *(ULONGLONG *)AbsNTTime;
    }

    // add offset
    LocalAbsNTTime += Offset * (ULONGLONG)10000000;

    // now convert to NT timestamp
    Error = NTTimeToNTPTime( NTPTimeStamp, (PFILETIME)&LocalAbsNTTime );

    DhcpAssert( ERROR_SUCCESS == Error );
    return;
}

VOID
DhcpNTPToNTTime(
    PULONG          NTPTimeStamp,
    DWORD           Offset,
    DATE_TIME       *NTTime
    )
/*++

Routine Description:

    The function calculates the absolute NTP timestamp from AbsTime on
    NT added by given offset.

Arguments:

    AbsNTTime - AbsTime on NT. If 0, it will use current time.

    RelativeOffset - offset to be added to AnsNTTime (in seconds.)

Return Value:

    The time in RelativeTime seconds from the current system time.

--*/
{
    ULONGLONG LocalAbsNTTime;
    DWORD       Error;

    Error = NTPTimeToNTFileTime(
                NTPTimeStamp,
                (FILETIME *)&LocalAbsNTTime,
                FALSE                           // not in host order.
                );

    DhcpAssert( ERROR_SUCCESS == Error );

    // add offset
    LocalAbsNTTime += Offset * (ULONGLONG)10000000;

    // now convert to NT timestamp
    // MBUG

    *(ULONGLONG *)NTTime = LocalAbsNTTime;
    return;
}


DWORD
DhcpReportEventW(
    LPWSTR Source,
    DWORD EventID,
    DWORD EventType,
    DWORD NumStrings,
    DWORD DataLength,
    LPWSTR *Strings,
    LPVOID Data
    )
/*++

Routine Description:

    This function writes the specified (EventID) log at the end of the
    eventlog.

Arguments:

    Source - Points to a null-terminated string that specifies the name
             of the module referenced. The node must exist in the
             registration database, and the module name has the
             following format:

                \EventLog\System\Lanmanworkstation

    EventID - The specific event identifier. This identifies the
                message that goes with this event.

    EventType - Specifies the type of event being logged. This
                parameter can have one of the following

                values:

                    Value                       Meaning

                    EVENTLOG_ERROR_TYPE         Error event
                    EVENTLOG_WARNING_TYPE       Warning event
                    EVENTLOG_INFORMATION_TYPE   Information event

    NumStrings - Specifies the number of strings that are in the array
                    at 'Strings'. A value of zero indicates no strings
                    are present.

    DataLength - Specifies the number of bytes of event-specific raw
                    (binary) data to write to the log. If cbData is
                    zero, no event-specific data is present.

    Strings - Points to a buffer containing an array of null-terminated
                strings that are merged into the message before
                displaying to the user. This parameter must be a valid
                pointer (or NULL), even if cStrings is zero.

    Data - Buffer containing the raw data. This parameter must be a
            valid pointer (or NULL), even if cbData is zero.


Return Value:

    Returns the WIN32 extended error obtained by GetLastError().

    NOTE : This function works slow since it calls the open and close
            eventlog source everytime.

--*/
{
    HANDLE EventlogHandle;
    DWORD ReturnCode;


    //
    // open eventlog section.
    //

    EventlogHandle = RegisterEventSourceW(
                    NULL,
                    Source
                    );

    if (EventlogHandle == NULL) {

        ReturnCode = GetLastError();
        goto Cleanup;
    }


    //
    // Log the error code specified
    //

    if( !ReportEventW(
            EventlogHandle,
            (WORD)EventType,
            0,            // event category
            EventID,
            NULL,
            (WORD)NumStrings,
            DataLength,
            Strings,
            Data
            ) ) {

        ReturnCode = GetLastError();
        goto Cleanup;
    }

    ReturnCode = NO_ERROR;

Cleanup:

    if( EventlogHandle != NULL ) {

        DeregisterEventSource(EventlogHandle);
    }

    return ReturnCode;
}


DWORD
DhcpReportEventA(
    LPWSTR Source,
    DWORD EventID,
    DWORD EventType,
    DWORD NumStrings,
    DWORD DataLength,
    LPSTR *Strings,
    LPVOID Data
    )
/*++

Routine Description:

    This function writes the specified (EventID) log at the end of the
    eventlog.

Arguments:

    Source - Points to a null-terminated string that specifies the name
             of the module referenced. The node must exist in the
             registration database, and the module name has the
             following format:

                \EventLog\System\Lanmanworkstation

    EventID - The specific event identifier. This identifies the
                message that goes with this event.

    EventType - Specifies the type of event being logged. This
                parameter can have one of the following

                values:

                    Value                       Meaning

                    EVENTLOG_ERROR_TYPE         Error event
                    EVENTLOG_WARNING_TYPE       Warning event
                    EVENTLOG_INFORMATION_TYPE   Information event

    NumStrings - Specifies the number of strings that are in the array
                    at 'Strings'. A value of zero indicates no strings
                    are present.

    DataLength - Specifies the number of bytes of event-specific raw
                    (binary) data to write to the log. If cbData is
                    zero, no event-specific data is present.

    Strings - Points to a buffer containing an array of null-terminated
                strings that are merged into the message before
                displaying to the user. This parameter must be a valid
                pointer (or NULL), even if cStrings is zero.

    Data - Buffer containing the raw data. This parameter must be a
            valid pointer (or NULL), even if cbData is zero.


Return Value:

    Returns the WIN32 extended error obtained by GetLastError().

    NOTE : This function works slow since it calls the open and close
            eventlog source everytime.

--*/
{
    HANDLE EventlogHandle;
    DWORD ReturnCode;


    //
    // open eventlog section.
    //

    EventlogHandle = RegisterEventSourceW(
                    NULL,
                    Source
                    );

    if (EventlogHandle == NULL) {

        ReturnCode = GetLastError();
        goto Cleanup;
    }


    //
    // Log the error code specified
    //

    if( !ReportEventA(
            EventlogHandle,
            (WORD)EventType,
            0,            // event category
            EventID,
            NULL,
            (WORD)NumStrings,
            DataLength,
            Strings,
            Data
            ) ) {

        ReturnCode = GetLastError();
        goto Cleanup;
    }

    ReturnCode = NO_ERROR;

Cleanup:

    if( EventlogHandle != NULL ) {

        DeregisterEventSource(EventlogHandle);
    }

    return ReturnCode;
}


DWORD
DhcpLogUnknownOption(
    LPWSTR Source,
    DWORD EventID,
    LPOPTION Option
    )
/*++

Routine Description:

    This routine logs an unknown DHCP option to event log.

Arguments:

    Source - name of the app that logs this error. it should be either
        "DhcpClient" or "DhcpServer".

    EventID - Event identifier number.

    Option - pointer to the unknown option structure.

Return Value:

    Windows Error code.

--*/
{
    LPWSTR  Strings[2];
    WCHAR StringsBuffer[ 2 * (3 + 1) ];
        // for two string each is 1byte decimal number (0 - 255).

    LPWSTR StringsPtr = StringsBuffer;

    //
    // convert option number.
    //

    Strings[0] = StringsPtr;
    DhcpDecimalToString( StringsPtr, Option->OptionType );
    StringsPtr += 3;

    *StringsPtr++ = L'\0';

    //
    // convert option length.
    //
    Strings[1] = StringsPtr;
    DhcpDecimalToString( StringsPtr, Option->OptionLength );
    StringsPtr += 3;

    *StringsPtr++ = L'\0';


    //
    // log error.
    //

    return(
        DhcpReportEventW(
            Source,
            EventID,
            EVENTLOG_WARNING_TYPE,
            2,
            (DWORD)Option->OptionLength,
            Strings,
            (PVOID)Option->OptionValue )
        );
}

#if DBG

VOID
DhcpAssertFailed(
    LPSTR FailedAssertion,
    LPSTR FileName,
    DWORD LineNumber,
    LPSTR Message
    )
/*++

Routine Description:

    Assertion failed.

Arguments:

    FailedAssertion :

    FileName :

    LineNumber :

    Message :

Return Value:

    none.

--*/
{
#ifndef DHCP_NOASSERT
    RtlAssert(
            FailedAssertion,
            FileName,
            (ULONG) LineNumber,
            (PCHAR) Message);
#endif

    DhcpPrint(( 0, "Assert @ %s \n", FailedAssertion ));
    DhcpPrint(( 0, "Assert Filename, %s \n", FileName ));
    DhcpPrint(( 0, "Line Num. = %ld.\n", LineNumber ));
    DhcpPrint(( 0, "Message is %s\n", Message ));

}

#endif



LPWSTR
DhcpRegIpAddressToKey(
    DHCP_IP_ADDRESS IpAddress,
    LPWSTR KeyBuffer
    )
/*++

Routine Description:

    This function converts an IpAddress to registry key. The registry
    key is unicode string of IpAddress in dotted form.

Arguments:

    IpAddress : IpAddress that needs conversion. The IpAddress is in
                host order.

    KeyBuffer : pointer a buffer that will hold the converted
                registry key. The buffer should be big enough to
                converted key.

Return Value:

    Pointer to the key the buffer.

--*/
{
    LPSTR OemKey;
    LPWSTR UnicodeKey;
    DWORD NetworkOrderIpAddress;

    NetworkOrderIpAddress = htonl(IpAddress);

    OemKey = inet_ntoa( *(struct in_addr *)&NetworkOrderIpAddress );
    UnicodeKey = DhcpOemToUnicode( OemKey, KeyBuffer );

    DhcpAssert( UnicodeKey == KeyBuffer );

    return( UnicodeKey );

}



DHCP_IP_ADDRESS
DhcpRegKeyToIpAddress(
    LPWSTR Key
    )
/*++

Routine Description:

    This function converts registry key to Ip Address.

Arguments:

    Key : Pointer to registry key.

Return Value:

    Converted IpAddress.

--*/
{
    CHAR OemKeyBuffer[DHCP_IP_KEY_LEN];
    LPSTR OemKey;


    OemKey = DhcpUnicodeToOem( Key, OemKeyBuffer );
    DhcpAssert( OemKey == OemKeyBuffer );

    return( ntohl(inet_addr( OemKey )) );
}



LPWSTR
DhcpRegOptionIdToKey(
    DHCP_OPTION_ID OptionId,
    LPWSTR KeyBuffer
    )
/*++

Routine Description:

    This function converts an OptionId to registry key. The registry
    key is unicode string of OptionId, 3 unicode char. long and of the
    form L"000".

Arguments:

    IpAddress : IpAddress that needs conversion.

    KeyBuffer : pointer a buffer that will hold the converted
                registry key. The buffer should be at least 8 char.
                long.

Return Value:

    Pointer to the key the buffer.

--*/

{
    int i;

    for (i = 2; i >= 0; i--) {
        KeyBuffer[i] = L'0' + (BYTE)(OptionId % 10 );
        OptionId /= 10;
    }
    KeyBuffer[3] = L'\0';

    return( KeyBuffer );
}



DHCP_OPTION_ID
DhcpRegKeyToOptionId(
    LPWSTR Key
    )
/*++

Routine Description:

    This function converts registry key to OptionId.

Arguments:

    Key : Pointer to registry key.

Return Value:

    Converted OptionId.

--*/

{
    DHCP_OPTION_ID OptionId = 0;
    int i;

    for (i = 0; i < 3 && Key[i] != L'\0'; i++) {
        OptionId = (OptionId * 10) + (Key[i] - L'0');
    }
    return( OptionId );
}

DWORD
DhcpStartWaitableTimer(
    HANDLE TimerHandle,
    DWORD SleepTime)
/*++

Routine Description:

    This routine starts the waitable timer. This timer fires off even
    when the system is in hibernate state.

Arguments

    TimerHandle - Waitable Timer Handle

    SleepTime   - Sleep Time in seconds.

Return Value:

    Status of the operation.

--*/
{
    DATE_TIME       SleepTimeInNSec; // sleep time in nano seconds since Jan 1 1901
    DWORD           Error;
    BOOL            Result;

    Error = STATUS_SUCCESS;
    SleepTimeInNSec = DhcpCalculateTime( SleepTime );

    Result = SetWaitableTimer(
                TimerHandle,            // handle to timer object
                (LARGE_INTEGER *)&SleepTimeInNSec,       // due time.
                0,                      // not periodic
                NULL,                   // completion routine
                NULL,                   // completion routine arg
                TRUE                    // resume power state when due
                );
    if ( !Result ) {
        DhcpPrint((0, "SetWaitableTimer reported Error = %d\n",Error=GetLastError()));
    }
    return Error;
}

VOID
DhcpCancelWaitableTimer(
    HANDLE TimerHandle
    )
/*++

Routine Description:

    This routine cancels the waitable timer.

Arguments

    TimerHandle - Waitable Timer Handle

Return Value:


--*/
{
    BOOL Result;

    Result = CancelWaitableTimer( TimerHandle );
    if ( !Result ) {
        DhcpPrint((0,"SetWaitableTimer reported Error = %lx\n",GetLastError()));
    }
}