/*++

Copyright (c) 1994  Microsoft Corporation

Module Name:

    mdhccapi.c

Abstract:

    This file contains the client side APIs for the MCAST.

Author:

    Munil Shah (munils)  02-Sept-97

Environment:

    User Mode - Win32

Revision History:


--*/
#include "precomp.h"
#include <dhcploc.h>
#include <dhcppro.h>
#include "mdhcpcli.h"

DWORD
MadcapInitGlobalData(
    VOID
    );

VOID
MadcapCleanupGlobalData(
    VOID
    );

DWORD
APIENTRY
McastApiStartup(
    IN  OUT PDWORD   pVersion
    )
/*++

Routine Description:

    This routine returns the current version of the apis and allocates any
    necessary resources.

Arguments:

    pVersion    - Version of the api clients. On return contains version of the
                    api implementation.

Return Value:

    ERROR_NOT_SUPPORTED if client version greater than impl version.
    (other Win32 errors)

--*/
{
    DWORD   Error;

    Error = ERROR_SUCCESS;
    if (!pVersion) {
        return ERROR_INVALID_PARAMETER;
    }
    // do we support this client version?
    if (*pVersion > MCAST_API_CURRENT_VERSION) {
        // not supported
        Error = ERROR_NOT_SUPPORTED;
    } else {
        // if client specified its version, use that
        // o/w assume version 1.0
        if (*pVersion) {
            gMadcapClientApplVersion = *pVersion;
        } else {
            gMadcapClientApplVersion = MCAST_API_VERSION_1;
        }
    }
    *pVersion = MCAST_API_CURRENT_VERSION;

    if( ERROR_SUCCESS == Error ) {
        Error = MadcapInitGlobalData();
        if (ERROR_SUCCESS != Error) {
            DhcpPrint((DEBUG_ERRORS, "McastApiStartup - Could not allocate resources %ld\n", Error));
            Error = ERROR_NO_SYSTEM_RESOURCES;
        }
    }

    return Error;
}

VOID
APIENTRY
McastApiCleanup(
    VOID
    )
/*++

Routine Description:

    This routine de-allocates resources allocated by the Startup routine.
    It must be called only AFTER a successful call to McastApiStartup.

--*/
{
    MadcapCleanupGlobalData();
}


DWORD
APIENTRY
McastEnumerateScopes(
    IN     IP_ADDR_FAMILY     AddrFamily,
    IN     BOOL               ReQuery,
    IN OUT PMCAST_SCOPE_ENTRY       pScopeList,
    IN OUT PDWORD             pScopeLen,
    OUT    PDWORD             pScopeCount
    )
/*++

Routine Description:

    This routine enumerates the multicast scopes available on the network.

Arguments:

    AddrFamily - AF_INET for IPv4 and AF_INET6 for IPv6

    ReQuery    - TRUE if the calls wants the list to be requried. FALSE o/w.

    pScopeList - pointer to the buffer where the scopelist is to be retrieved.
                 This parameter can be NULL if only the length of the buffer is
                 being retrieved.

                 When this buffer is NULL, the API will force the re-query of the
                 scope list from the MCAST servers.

    pScopeLen  - Pointer to a variable that specifies the size, in bytes, of the
                 buffer pointed to by the pScopeList parameter. When the function returns,
                 this variable contains the size of the data copied to pScopeList;

                 The pScopeLen parameter can not be NULL.

                 If the buffer specified by pScopeList parameter is not large enough
                 to hold the data, the function returns the value ERROR_MORE_DATA, and
                 stores the required buffer size, in bytes, into the variable pointed
                 to by pScopeLen.

                 If pScopeList is NULL, and pScopeLen is non-NULL, the function returns
                 ERROR_SUCCESS, and stores the size of the data, in bytes, in the variable
                 pointed to by pScopeLen. This lets an application determine the best
                 way to allocate a buffer for the scope list.

    pScopeCount - Pointer to a variable that will store total number of scopes returned
                 in the pScopeList buffer.

Return Value:

    The status of the operation.

--*/
{
    DWORD   Error;


    // First check the validity of the arguments.

    // has startup been called?
    if ( !gMadcapClientApplVersion ) {
        DhcpPrint((DEBUG_ERRORS, "McastEnumerateScopes - Not ready. Client Version %d\n",
                   gMadcapClientApplVersion));
        return ERROR_NOT_READY;
    }

    // Correct addr family?
    if (AF_INET != AddrFamily) {
        DhcpPrint((DEBUG_ERRORS, "McastEnumerateScopes - Invalid AddrFamily IPv%d\n", AddrFamily));
        return ERROR_INVALID_PARAMETER;
    }

    // pScopeLen can not be NULL.
    if ( !pScopeLen || IsBadWritePtr( pScopeLen, sizeof(DWORD) ) ) {
        DhcpPrint((DEBUG_ERRORS, "McastEnumerateScopes - Invalid ScopeLen param\n"));
        return ERROR_INVALID_PARAMETER;
    }
    // if pScopeList buffer is given, then pScopeCount can not be NULL.
    if ( pScopeList &&
         (!pScopeCount || IsBadWritePtr( pScopeCount, sizeof(DWORD)) ) ) {
        DhcpPrint((DEBUG_ERRORS, "McastEnumerateScopes - Invalid ScopeCount param\n"));
        return ERROR_INVALID_PARAMETER;
    }

    // if we are not requerying the list then pScopList can not be NULL.
    if (!ReQuery &&
        (!pScopeList || IsBadWritePtr( pScopeList, *pScopeLen ) ) ) {
        DhcpPrint((DEBUG_ERRORS, "McastEnumerateScopes - Invalid ScopeList & ReQuery param\n"));
        return ERROR_INVALID_PARAMETER;
    }

    // initialize the status.
    Error = STATUS_SUCCESS;

    // do we need to requery ?
    if ( ReQuery ) {
        // query the MCAST servers and get the new list of MScopes.
        Error = ObtainMScopeList();
        if ( ERROR_SUCCESS != Error ) {
            return Error;
        }
    } else {
        if( !gMadcapScopeList ) {
            return ERROR_NO_DATA;
        }
    }

    // Has the client specified the buffer?
    if ( pScopeList ) {
        // yes, copy the scopes.
        DhcpPrint((DEBUG_API, "McastEnumerateScopes - Copying existing mscope list\n"));
        return CopyMScopeList(
                    pScopeList,
                    pScopeLen,
                    pScopeCount );
    } else {
        // no, just return the length of the scope list and the scope count.
        LOCK_MSCOPE_LIST();
        if( gMadcapScopeList != NULL ) {
            *pScopeLen = gMadcapScopeList->ScopeLen;
            if ( pScopeCount ) *pScopeCount = gMadcapScopeList->ScopeCount;
            Error = ERROR_SUCCESS;
        } else {
            Error = ERROR_NO_DATA;
        }
        UNLOCK_MSCOPE_LIST();
    }

    return Error;
}


DWORD
APIENTRY
McastGenUID(
    IN     LPMCAST_CLIENT_UID   pRequestID
    )
/*++

Routine Description:

    This routine generates the unique identifier which client can use to later pass
    to request/renew addresses.

Arguments:

    pRequestID - Pointer to the UID struct where the identifier is to be stored. The
                buffer that holds the id should be at-least MCAST_CLIENT_ID_LEN long.

Return Value:

    The status of the operation.

--*/
{
    if (!pRequestID) {
        return ERROR_INVALID_PARAMETER;
    }

    if (!pRequestID->ClientUID || IsBadWritePtr( pRequestID->ClientUID, pRequestID->ClientUIDLength) ) {
        return ERROR_INVALID_PARAMETER;
    }

    return GenMadcapClientUID( pRequestID->ClientUID, &pRequestID->ClientUIDLength );

}

DWORD
APIENTRY
McastRequestAddress(
    IN     IP_ADDR_FAMILY           AddrFamily,
    IN     LPMCAST_CLIENT_UID       pRequestID,
    IN     PMCAST_SCOPE_CTX         pScopeCtx,
    IN     PMCAST_LEASE_REQUEST     pAddrRequest,
    IN OUT PMCAST_LEASE_RESPONSE    pAddrResponse
    )
/*++

Routine Description:

    This routine request multicast address(es) from the MCAST server.

Arguments:

    AddrFamily - AF_INET for IPv4 and AF_INET6 for IPv6

    pRequestID - Unique identifier for this request. Client is responsible for
                 generating unique identifier for every request. One recommendation
                 is to use application specific context hashed by time.

    pRequestIDLen - Length of the pRequestID buffer.

    pScopeCtx  - Pointer to the context of the scope from which the address is to
                 be allocated. Scope context has to be retrieved via McastEnumerateScopes
                 call before calling this.

    pAddrRequest - Pointer to the block containing all the parameters pertaining
                 to multicast address request. The MCAST_API_VERSION_1 version of
                 implementation supports allocation of only one address at a time.
                 So the AddrCount  and MinAddrCount value must be 1.ServerAddress
                 field is ignored.

    pAddrResponse - Pointer to the block which contains the response paramters for
                 the multicast address request. The caller is responsible for allocating
                 the space for pAddrBuf for the requested number of addresses and
                 setting the pointer to that space.

Return Value:

    The status of the operation.

--*/
{
    PDHCP_CONTEXT  pContext = NULL;
    DWORD   Error;
    DWORD   ScopeId;
    time_t  TimeNow;
    time_t  LocalLeaseStartTime;
    time_t  LocalMaxLeaseStartTime;

    // do some param checking.

    // has startup been called?
    if ( !gMadcapClientApplVersion ) {
        DhcpPrint((DEBUG_ERRORS, "McastRequestAddress - Not ready. Client Version %d\n",
                   gMadcapClientApplVersion));
        return ERROR_NOT_READY;
    }

    if (AF_INET != AddrFamily) {
        DhcpPrint((DEBUG_ERRORS, "McastRequestAddress - Invalid AddrFamily IPv%d\n", AddrFamily));
        return ERROR_INVALID_PARAMETER;
    }

    if ( !pRequestID || !pRequestID->ClientUID || !pAddrRequest || !pAddrResponse ) {
        DhcpPrint((DEBUG_ERRORS,"McastRequestAddress - one of parameter is NULL\n"));
        return ERROR_INVALID_PARAMETER;
    }

    if ( pAddrRequest->AddrCount != 1 || pAddrResponse->AddrCount < pAddrRequest->AddrCount) {
        DhcpPrint((DEBUG_ERRORS,"McastRequestAddress - currently support one address - requested %ld\n",
                   pAddrRequest->AddrCount));
        return ERROR_INVALID_PARAMETER;
    }


    if ( pAddrRequest->pAddrBuf &&
         (*(DWORD UNALIGNED *)pAddrRequest->pAddrBuf) &&
         !CLASSD_NET_ADDR(*(DWORD UNALIGNED *)pAddrRequest->pAddrBuf)) {
        DhcpPrint((DEBUG_ERRORS,"McastRequestAddress - requested address not valid %s\n",
                   DhcpIpAddressToDottedString(*(DWORD UNALIGNED *)pAddrRequest->pAddrBuf)));
        return ERROR_INVALID_PARAMETER;
    }

    if ( !pAddrResponse->pAddrBuf ) {
        DhcpPrint((DEBUG_ERRORS,"McastRequestAddress - response buffer has null pAddrBuf\n"));
        return ERROR_INVALID_PARAMETER;
    }

    if (pRequestID->ClientUIDLength < MCAST_CLIENT_ID_LEN) {
        DhcpPrint((DEBUG_ERRORS,"McastRequestAddress - requestid length %d too small\n",
                   pRequestID->ClientUIDLength));
        return ERROR_INVALID_PARAMETER;
    }

    if ( !pScopeCtx ) {
        DhcpPrint((DEBUG_ERRORS, "McastRequestAddress - scope context not supplied\n"));
        return ERROR_INVALID_PARAMETER;
    }

    time(&TimeNow);
    LocalLeaseStartTime = LocalMaxLeaseStartTime = TimeNow;
    if ( pAddrRequest->LeaseStartTime > LocalLeaseStartTime ) {
        LocalLeaseStartTime = pAddrRequest->LeaseStartTime;
    }
    if ( pAddrRequest->MaxLeaseStartTime > LocalMaxLeaseStartTime ) {
        LocalMaxLeaseStartTime = pAddrRequest->MaxLeaseStartTime;
    }

    if ( LocalLeaseStartTime > LocalMaxLeaseStartTime ) {
        DhcpPrint((DEBUG_ERRORS,"McastRequestAddress - invalid start lease times\n"));
        return ERROR_INVALID_PARAMETER;
    }

    if ( pAddrRequest->LeaseDuration < pAddrRequest->MinLeaseDuration ) {
        DhcpPrint((DEBUG_ERRORS,"McastRequestAddress - invalid lease duration\n"));
        return ERROR_INVALID_PARAMETER;
    }

    Error = CreateMadcapContext(&pContext, pRequestID, pScopeCtx->Interface.IpAddrV4 );
    if ( ERROR_SUCCESS != Error ) goto Cleanup;
    APICTXT_ENABLED(pContext);                 // mark the context as being created by the API

    if (pAddrRequest->pAddrBuf && (*(DWORD UNALIGNED *)pAddrRequest->pAddrBuf) ) {
        pContext->DesiredIpAddress = *(DWORD UNALIGNED *)pAddrRequest->pAddrBuf;
    }
    //pContext->DhcpServerAddress = pScopeCtx->ServerID;

    Error = ObtainMadcapAddress(
                pContext,
                &pScopeCtx->ScopeID,
                pAddrRequest,
                pAddrResponse
                );

Cleanup:
    if ( pContext )
        DhcpDestroyContext( pContext );

    return Error;
}

DWORD
APIENTRY
McastRenewAddress(
    IN     IP_ADDR_FAMILY           AddrFamily,
    IN     LPMCAST_CLIENT_UID       pRequestID,
    IN     PMCAST_LEASE_REQUEST     pRenewRequest,
    IN OUT PMCAST_LEASE_RESPONSE    pRenewResponse
    )
/*++

Routine Description:

    This routine renews multicast address(es) from the MCAST server.

Arguments:

    AddrFamily - AF_INET for IPv4 and AF_INET6 for IPv6

    pRequestID - Unique identifier that was used when the address(es) were
                 obtained initially.

    RequestIDLen - Length of the pRequestID buffer.

    pRenewRequest - Pointer to the block containing all the parameters pertaining
                 to the renew request.

    pRenewResponse - Pointer to the block which contains the response paramters for
                 the renew request.The caller is responsible for allocating the
                 space for pAddrBuf for the requested number of addresses and
                 setting the pointer to that space.

Return Value:

    The status of the operation.

--*/
{

    PDHCP_CONTEXT  pContext = NULL;
    DWORD           Error;
    DHCP_IP_ADDRESS SelectedServer;
    DWORD           ScopeId;
    time_t  TimeNow;

    // do some param checking.

    // has startup been called?
    if ( !gMadcapClientApplVersion ) {
        DhcpPrint((DEBUG_ERRORS, "McastRenewAddress - Not ready. Client Version %d\n",
                   gMadcapClientApplVersion));
        return ERROR_NOT_READY;
    }

    if (AF_INET != AddrFamily) {
        DhcpPrint((DEBUG_ERRORS, "McastRenewAddress - Invalid AddrFamily IPv%d\n", AddrFamily));
        return ERROR_INVALID_PARAMETER;
    }

    if ( !pRequestID || !pRenewRequest || !pRenewResponse ) {
        DhcpPrint((DEBUG_ERRORS,"McastRenewAddress - one of parameter is NULL\n"));
        return ERROR_INVALID_PARAMETER;
    }

    if ( pRenewRequest->AddrCount != 1 ||
          pRenewResponse->AddrCount < pRenewRequest->AddrCount ||
         !pRenewResponse->pAddrBuf ||
         !pRenewRequest->pAddrBuf ||
         !CLASSD_NET_ADDR( *(DWORD UNALIGNED *)pRenewRequest->pAddrBuf) ) {
        DhcpPrint((DEBUG_ERRORS,"McastRenewAddress - address %s type V%d count %ld is invalid\n",
                   DhcpIpAddressToDottedString( *(DWORD UNALIGNED *)pRenewRequest->pAddrBuf),
                   pRenewRequest->AddrCount ));
        return ERROR_INVALID_PARAMETER;
    }

    if (!pRenewRequest->ServerAddress.IpAddrV4) {
        DhcpPrint((DEBUG_ERRORS,"McastRequestAddress - server address not specified \n"));
        return ERROR_INVALID_PARAMETER;
    }
    if (pRequestID->ClientUIDLength < MCAST_CLIENT_ID_LEN) {
        DhcpPrint((DEBUG_ERRORS,"McastRenewAddress - requestid length too small\n",
                   pRequestID->ClientUIDLength));
        return ERROR_INVALID_PARAMETER;
    }

    time(&TimeNow);
    if ( pRenewRequest->LeaseStartTime > pRenewRequest->MaxLeaseStartTime ||
         (pRenewRequest->LeaseDuration < pRenewRequest->MinLeaseDuration)) {
        DhcpPrint((DEBUG_ERRORS,"McastRenewAddress - invalid lease times\n"));
        return ERROR_INVALID_PARAMETER;
    }

    Error = CreateMadcapContext(&pContext, pRequestID, INADDR_ANY);
    if ( ERROR_SUCCESS != Error) return  Error;
    APICTXT_ENABLED(pContext);          // mark the context as being created by the API

    pContext->DesiredIpAddress = *(DWORD UNALIGNED *)pRenewRequest->pAddrBuf;
    pContext->DhcpServerAddress = pRenewRequest->ServerAddress.IpAddrV4;


    Error = RenewMadcapAddress(
                pContext,
                NULL,
                pRenewRequest,
                pRenewResponse,
                0
                );

Cleanup:

    if ( pContext ) DhcpDestroyContext( pContext );
    return Error;
}

DWORD
APIENTRY
McastReleaseAddress(
    IN     IP_ADDR_FAMILY          AddrFamily,
    IN     LPMCAST_CLIENT_UID      pRequestID,
    IN     PMCAST_LEASE_REQUEST    pReleaseRequest
    )
/*++

Routine Description:

    This routine releases multicast address(es) from the MCAST server.

Arguments:

    AddrFamily - AF_INET for IPv4 and AF_INET6 for IPv6

    pRequestID - Unique identifier that was used when the address(es) were
                 obtained initially.

    pReleaseRequest - Pointer to the block containing all the parameters pertaining
                 to the release request.

Return Value:

    The status of the operation.

--*/
{
    PDHCP_CONTEXT  pContext = NULL;
    DWORD           Error;
    DHCP_IP_ADDRESS SelectedServer;
    DWORD           ScopeId;

    // do some param checking.

    // has startup been called?
    if ( !gMadcapClientApplVersion ) {
        DhcpPrint((DEBUG_ERRORS, "McastReleaseAddress - Not ready. Client Version %d\n",
                   gMadcapClientApplVersion));
        return ERROR_NOT_READY;
    }

    if (AF_INET != AddrFamily) {
        DhcpPrint((DEBUG_ERRORS, "McastReleaseAddress - Invalid AddrFamily IPv%d\n", AddrFamily));
        return ERROR_INVALID_PARAMETER;
    }

    if ( !pRequestID || !pReleaseRequest ) {
        DhcpPrint((DEBUG_ERRORS,"McastReleaseAddress - one of parameter is NULL\n"));
        return ERROR_INVALID_PARAMETER;
    }

    if ( pReleaseRequest->AddrCount != 1 ||
         !pReleaseRequest->pAddrBuf ||
         !CLASSD_NET_ADDR( *(DWORD UNALIGNED *)pReleaseRequest->pAddrBuf) ) {
        DhcpPrint((DEBUG_ERRORS,"McastReleaseAddress - address %s count %ld is invalid\n",
                   DhcpIpAddressToDottedString( *(DWORD UNALIGNED *)pReleaseRequest->pAddrBuf), pReleaseRequest->AddrCount ));
        return ERROR_INVALID_PARAMETER;
    }

    if (!pReleaseRequest->ServerAddress.IpAddrV4) {
        DhcpPrint((DEBUG_ERRORS,"McastReleaseAddress - server address is invalid\n"));
        return ERROR_INVALID_PARAMETER;
    }

    if (pRequestID->ClientUIDLength < MCAST_CLIENT_ID_LEN) {
        DhcpPrint((DEBUG_ERRORS,"McastRequestAddress - requestid length too small\n",
                   pRequestID->ClientUIDLength));
        return ERROR_INVALID_PARAMETER;
    }

    Error = CreateMadcapContext(&pContext, pRequestID, INADDR_ANY );
    if ( ERROR_SUCCESS != Error) return  Error;
    APICTXT_ENABLED(pContext);                 // mark the context as being created by the API

    pContext->DhcpServerAddress = pReleaseRequest->ServerAddress.IpAddrV4;

    Error = ReleaseMadcapAddress(pContext);

Cleanup:

    if ( pContext ) DhcpDestroyContext( pContext );
    return Error;
}