/* Copyright (c) 1998-1999 Microsoft Corporation Module Name: CMDhcp.cpp Abstract: Implementation of CMdhcp. Author: */ #include "stdafx.h" #include #include "mdhcp.h" #include "CMDhcp.h" #include "lease.h" #include "local.h" // template for collections #include "collect.h" // From rendezvous control code: // sets the first bit to indicate error // sets the win32 facility code // this is used instead of the HRESULT_FROM_WIN32 macro // because that clears the customer flag inline long HRESULT_FROM_ERROR_CODE(IN long ErrorCode) { return ( 0x80070000 | (0xa000ffff & ErrorCode) ); } ///////////////////////////////////////////////////////////////////////////// // Helper functions. ///////////////////////////////////////////////////////////////////////////// HRESULT CMDhcp::CreateWrappers( DWORD dwScopeCount, // the number of scopes we were given MCAST_SCOPE_ENTRY * pScopeList, // array of scope structs IMcastScope *** pppWrappers, // here we will put an array of if ptrs BOOL fLocal // true = scopes are locally generated ) { LOG((MSP_TRACE, "CMDhcp::CreateWrappers enter")); HRESULT hr; // Allocate the array of interface pointers. typedef IMcastScope * ScopeIfPtr; *pppWrappers = new ScopeIfPtr[dwScopeCount]; if ( (*pppWrappers) == NULL ) { LOG((MSP_ERROR, "can't create allocate array of interface pointers")); return E_OUTOFMEMORY; } // For each scope in the list of scopes returned by the C API for (DWORD i = 0; i < dwScopeCount; i++) { // create the com object. CComObject * pMDhcpScope; hr = CComObject::CreateInstance(&pMDhcpScope); if ( (FAILED(hr)) || (NULL == pMDhcpScope) ) { LOG((MSP_ERROR, "can't create MDhcpScope Object (%d/%d): %08x", i, dwScopeCount, hr)); // get rid of all previously created COM objects for (DWORD j = 0; j < i; j++) (*pppWrappers)[j]->Release(); delete (*pppWrappers); return hr; } // Get the IMcastScope interface. hr = pMDhcpScope->_InternalQueryInterface( IID_IMcastScope, (void **) (& (*pppWrappers)[i]) ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CreateWrappers:QueryInterface (%d/%d) failed: %08x", i, dwScopeCount, hr)); // get rid of all previously created COM objects for (DWORD j = 0; j < i; j++) (*pppWrappers)[j]->Release(); delete (*pppWrappers); delete pMDhcpScope; // don't know if it addrefed or not return hr; } // Set the object's info based on the struct. From now on the // object will be read-only. hr = pMDhcpScope->Initialize(pScopeList[i], fLocal); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CreateWrappers:Initialize (%d/%d) failed: %08x", i, dwScopeCount, hr)); // get rid of all previously created COM objects for (DWORD j = 0; j < i; j++) (*pppWrappers)[j]->Release(); delete (*pppWrappers); pMDhcpScope->Release(); // we know it addrefed in the QI return hr; } } LOG((MSP_TRACE, "CMDhcp::CreateWrappers exit")); return S_OK; } ///////////////////////////////////////////////////////////////////////////// // Get a list of scopes from the C API. HRESULT CMDhcp::GetScopeList( DWORD * pdwScopeCount, MCAST_SCOPE_ENTRY ** ppScopeList, BOOL * pfLocal ) { LOG((MSP_TRACE, "CMDhcp::GetScopeList enter")); _ASSERTE( ! IsBadWritePtr(pdwScopeCount, sizeof(DWORD) ) ); _ASSERTE( ! IsBadWritePtr(ppScopeList, sizeof(MCAST_SCOPE_ENTRY *) ) ); HRESULT hr; DWORD dwScopeLen = 0; // size in bytes of returned scopes structure DWORD dwCode; // return code *pfLocal = FALSE; // try mdhcp first dwCode = LocalEnumerateScopes(NULL, // only want to know how many we have &dwScopeLen, // # of bytes should be zero pdwScopeCount, // # of scopes placed here pfLocal); // This must succeed for us to continue. if (dwCode != ERROR_SUCCESS) { hr = HRESULT_FROM_ERROR_CODE(dwCode); LOG((MSP_ERROR, "GetScopeList: First C API call failed " "(code: %d hresult: %08x)", dwCode, hr)); return hr; } do { // If there are no scopes to choose from, let's not enumerate them. // We also need at least the length fields from the first // UNICODE_STRING. if ( (dwScopeLen < sizeof(MCAST_SCOPE_ENTRY)) || (*pdwScopeCount < 1) ) { LOG((MSP_ERROR, "GetScopeList: don't have enough scopes (%d;%d)", dwScopeLen, *pdwScopeCount)); return E_FAIL; } // Now that we know how many there are, allocate an array to hold the // scope structs returned by the C method. // The API acts very strangely here. We have to give it dwScopeLen // bytes as one big chunk. The first dwScopeCount * sizeof(MCAST_SCOPE_ENTRY) // bytes contain dwScopeCount MCAST_SCOPE_ENTRY structures. Each of these // structures has a pointer to a wide string. The first of these points // to the first byte after all the MCAST_SCOPE_ENTRY structures! In this way // they avoid doing so many mallocs. We therefore have to // copy each string in the COM wrapper for each scope, and then delete // this buffer (ppScopeList) all at once after all the wrapping is complete. *ppScopeList = (MCAST_SCOPE_ENTRY *) new CHAR[dwScopeLen]; if (*ppScopeList == NULL) { LOG((MSP_ERROR, "GetScopeList: not enough memory to allocate scope" " list (size = %d)", dwScopeLen)); return E_OUTOFMEMORY; } // *pdwScopeCount still specifies the number of scopes we can get. // Now ask for all the scopes. dwCode = LocalEnumerateScopes(*ppScopeList, &dwScopeLen, pdwScopeCount, pfLocal); // If things changed in this bried time, just try again. if (dwCode == ERROR_MORE_DATA) { LOG((MSP_INFO, "GetScopeList: got more scopes than we were told " "existed (we though there were %d) -- retrying", *pdwScopeCount)); delete (*ppScopeList); } } while (dwCode == ERROR_MORE_DATA); if (dwCode != ERROR_SUCCESS) { hr = HRESULT_FROM_ERROR_CODE(dwCode); LOG((MSP_ERROR, "GetScopeList: Second C API call failed " "(code: %d hresult: %08x)", dwCode, hr)); delete (*ppScopeList); return hr; } LOG((MSP_TRACE, "CMDhcp::GetScopeList exit")); return S_OK; } ///////////////////////////////////////////////////////////////////////////// // This is a private helper method that creates a CMDhcpLeaseInfo object and // uses it to wrap a lease info structure and request ID into an // IMcastLeaseInfo interface. HRESULT CMDhcp::WrapMDhcpLeaseInfo( BOOL fGotTtl, long lTtl, BOOL fLocal, MCAST_LEASE_INFO * pLeaseInfo, MCAST_CLIENT_UID * pRequestID, IMcastLeaseInfo ** ppInterface ) { LOG((MSP_TRACE, "CMDhcp::WrapMDhcpLeaseInfo enter")); // We don't check pLeaseInfo or pRequestID -- they'll be comprehensively // checked in the Wrap call below. if ( IsBadWritePtr(ppInterface, sizeof(IMcastLeaseInfo *) ) ) { LOG((MSP_ERROR, "WrapMDhcpLeaseInfo: invalid pointer: %x", ppInterface)); return E_POINTER; } HRESULT hr; // create the com object. CComObject * pMDhcpLeaseInfo; hr = CComObject::CreateInstance(&pMDhcpLeaseInfo); if ( (FAILED(hr)) || (pMDhcpLeaseInfo == NULL) ) { LOG((MSP_ERROR, "can't create MDhcpLeaseInfo Object.")); return hr; } // Get the IMcastLeaseInfo interface. hr = pMDhcpLeaseInfo->_InternalQueryInterface( IID_IMcastLeaseInfo, (void **)ppInterface ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "WrapMDhcpLeaseInfo:QueryInterface failed: %x", hr)); delete pMDhcpLeaseInfo; return hr; } // Wrap the object in the interface. hr = pMDhcpLeaseInfo->Wrap(pLeaseInfo, pRequestID, fGotTtl, lTtl); if ( FAILED(hr) ) { LOG((MSP_ERROR, "WrapMDhcpLeaseInfo:Wrap failed: %x", hr)); (*ppInterface)->Release(); return hr; } hr = pMDhcpLeaseInfo->SetLocal(fLocal); if ( FAILED(hr) ) { LOG((MSP_ERROR, "WrapMDhcpLeaseInfo: SetLocal failed: %x", hr)); (*ppInterface)->Release(); return hr; } LOG((MSP_TRACE, "CMDhcp::WrapMDhcpLeaseInfo exit")); return S_OK; } ///////////////////////////////////////////////////////////////////////////// // This is a private helper method that munges the arguments into structs // at the beginning of a Request call. HRESULT CMDhcp::PrepareArgumentsRequest( IN IMcastScope * pScope, IN DATE LeaseStartTime, IN DATE LeaseStopTime, IN long lNumAddresses, OUT MCAST_CLIENT_UID * pRequestIDStruct, OUT MCAST_SCOPE_CTX * pScopeCtxStruct, OUT MCAST_LEASE_INFO ** ppLeaseStruct, OUT BOOL * pfLocal, OUT long * plTtl ) { LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsRequest enter")); _ASSERTE ( ! IsBadReadPtr(pScope, sizeof(IMcastScope) ) ); _ASSERTE ( ! IsBadWritePtr(pRequestIDStruct, sizeof(MCAST_CLIENT_UID) ) ); _ASSERTE ( ! IsBadWritePtr(pScopeCtxStruct, sizeof(MCAST_SCOPE_CTX) ) ); _ASSERTE ( ! IsBadWritePtr(ppLeaseStruct, sizeof(MCAST_LEASE_INFO *) ) ); _ASSERTE ( ! IsBadWritePtr(pfLocal, sizeof(BOOL) ) ); _ASSERTE ( ! IsBadWritePtr(plTtl, sizeof(long) ) ); HRESULT hr; // // The start time must be less than the stop time. // if ( LeaseStartTime > LeaseStopTime ) { LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - " "start time is greater than stop time - exit E_INVALIDARG")); return E_INVALIDARG; } // // lNumAddresses must be stuffed into a WORD for the C API -- check to see if // it's in range. // if ( ( lNumAddresses < 0 ) || ( lNumAddresses > USHRT_MAX ) ) { LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - " "invalid number of addresses - exit E_INVALIDARG")); return E_INVALIDARG; } // // dynamic_cast to get an object pointer from the passed-in interface // pointer. This will cause an exception if the user tries to use their // own implementation of IMcastScope, which is quite unlikely. // CMDhcpScope * pCScope = dynamic_cast(pScope); if (pCScope == NULL) { LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - " "Unsupported CMDhcpScope object")); return E_POINTER; } // // Find out if this scope uses local alloc. // hr = pCScope->GetLocal(pfLocal); if (FAILED(hr)) { LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest: " "GetLocal failed %08x", hr)); return hr; } // // Find out the ttl to stuff in leases from this scope. // hr = pCScope->get_TTL( plTtl ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest: " "get_TTL failed %08x", hr)); return hr; } // // Get the normal scope info. // ScopeID is stored in network byte order but the get_ method // returns it in host byte order for the benefit of apps. // long lScopeID; hr = pScope->get_ScopeID( &lScopeID ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - " "can't get scope ID from scope object - exit 0x%08x", hr)); return hr; } pScopeCtxStruct->ScopeID.IpAddrV4 = htonl(lScopeID); hr = pScope->get_ServerID( (long *) &(pScopeCtxStruct->ServerID.IpAddrV4) ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - " "can't get server ID from scope object - exit 0x%08x", hr)); return hr; } hr = pScope->get_InterfaceID( (long *) &(pScopeCtxStruct->Interface.IpAddrV4) ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - " "can't get interface ID from scope object - exit 0x%08x", hr)); return hr; } // // Allocate space for the client UID. // pRequestIDStruct->ClientUIDLength = MCAST_CLIENT_ID_LEN; pRequestIDStruct->ClientUID = new BYTE[ MCAST_CLIENT_ID_LEN ]; if ( pRequestIDStruct->ClientUID == NULL ) { LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest: out of memory in " "buffer allocation")); return E_OUTOFMEMORY; } // // Generate a random client UID. // DWORD dwResult = McastGenUID( pRequestIDStruct ); if ( dwResult != ERROR_SUCCESS ) { hr = HRESULT_FROM_ERROR_CODE( dwResult ); LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsRequest: " "McastGenUID failed (dw = %d; hr = 0x%08x)", dwResult, hr)); return hr; } LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsRequest: before MCAST_LEASE_INFO " "alloc; we are asking for %d addresses", lNumAddresses)); // // Allocate the lease info structure. // The caller will delete it after the API call. // This is a REQUEST, so we do not specify any particular addresses // in the array -- we do not need space for them. // // (*ppLeaseStruct) = new MCAST_LEASE_INFO; if ( (*ppLeaseStruct) == NULL ) { LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest: out of memory in " "MCAST_LEASE_INFO allocation")); delete (pRequestIDStruct->ClientUID); return E_OUTOFMEMORY; } // // Fill in the times. // hr = DateToLeaseTime(LeaseStartTime, &((*ppLeaseStruct)->LeaseStartTime)); if ( FAILED(hr) ) { delete (pRequestIDStruct->ClientUID); delete (*ppLeaseStruct); return hr; } hr = DateToLeaseTime(LeaseStopTime, &((*ppLeaseStruct)->LeaseEndTime)); if ( FAILED(hr) ) { delete (pRequestIDStruct->ClientUID); delete (*ppLeaseStruct); return hr; } // // Fill in the address info fields. // (*ppLeaseStruct)->ServerAddress.IpAddrV4 = 0; (*ppLeaseStruct)->AddrCount = (WORD) lNumAddresses; // checked above // // This is a REQUEST, so we do not specify any particular addresses // in the array -- we make the array NULL. // (*ppLeaseStruct)->pAddrBuf = NULL; LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsRequest exit")); return S_OK; } ///////////////////////////////////////////////////////////////////////////// // This is a private helper method that munges the arguments into structs // at the beginning of a Renew or Release call. HRESULT CMDhcp::PrepareArgumentsNonRequest( IN IMcastLeaseInfo * pLease, OUT MCAST_CLIENT_UID * pRequestIDStruct, OUT MCAST_LEASE_INFO ** ppLeaseStruct, OUT BOOL * pfLocal, OUT BOOL * pfGotTtl, OUT long * plTtl ) { LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsNonRequest enter")); if ( IsBadReadPtr(pLease, sizeof(IMcastLeaseInfo) ) ) { LOG((MSP_ERROR, "PrepareArgumentsNonRequest: bad pLease pointer argument")); return E_POINTER; } _ASSERTE ( ! IsBadWritePtr(pRequestIDStruct, sizeof(MCAST_CLIENT_UID) ) ); _ASSERTE ( ! IsBadWritePtr(ppLeaseStruct, sizeof(MCAST_LEASE_INFO *) ) ); _ASSERTE ( ! IsBadWritePtr(pfLocal, sizeof(BOOL) ) ); _ASSERTE ( ! IsBadWritePtr(pfGotTtl, sizeof(BOOL) ) ); _ASSERTE ( ! IsBadWritePtr(plTtl, sizeof(long) ) ); HRESULT hr; // We approach things in a completely different way here, compared // to the other PrepareArguments method -- we use // dynamic_cast to get an object pointer from the passed-in interface // pointer. This will cause an exception if the user tries to use their // own implementation of IMcastRequestID, which is quite unlikely. CMDhcpLeaseInfo * pCLease = dynamic_cast(pLease); if (pCLease == NULL) { LOG((MSP_ERROR, "PrepareArgumentsNonRequest: Unsupported CMDhcpLeaseInfo object")); return E_POINTER; } // // Find out if this lease was obtained using local alloc. // hr = pCLease->GetLocal(pfLocal); if (FAILED(hr)) { LOG((MSP_ERROR, "PrepareArgumentsNonRequest: " "GetLocal failed %08x", hr)); return hr; } // // If the lease had a TTL set, then retrieve it for use in a // resulting response. Else just say we don't have a ttl. // hr = pCLease->get_TTL( plTtl ); *pfGotTtl = SUCCEEDED(hr); // // Get our request ID from the lease info object. // pRequestIDStruct->ClientUIDLength = MCAST_CLIENT_ID_LEN; pRequestIDStruct->ClientUID = new BYTE[ MCAST_CLIENT_ID_LEN ]; if (pRequestIDStruct->ClientUID == NULL) { LOG((MSP_ERROR, "PrepareArgumentsNonRequest: out of memory in " "buffer allocation")); return E_OUTOFMEMORY; } hr = pCLease->GetRequestIDBuffer(pRequestIDStruct->ClientUIDLength, pRequestIDStruct->ClientUID); if ( FAILED(hr) ) { LOG((MSP_ERROR, "PrepareArgumentsNonRequest: RequestID " "GetBuffer failed %08x", hr)); delete (pRequestIDStruct->ClientUID); return hr; } // // Get the rest of the stuff, which belongs in the straight lease info // structure, from the lease info object. // // this does a new for us hr = pCLease->GetStruct(ppLeaseStruct); if ( FAILED(hr) ) { LOG((MSP_ERROR, "PrepareArgumentsNonRequest - " "failed to grab pLeaseStruct - 0x%08x", hr)); delete (pRequestIDStruct->ClientUID); return hr; } LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsNonRequest exit")); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // VerifyAndGetArrayBounds // // Helper function for variant/safearrays // // Array // IN Variant that contains a safearray // // ppsa // OUT safearray returned here // // pllBound // OUT array lower bound returned here // // pluBound // OUT array upper bound returned here // // RETURNS // // verifies that Array contains an array, and returns the array, upper // and lower bounds. // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ static HRESULT VerifyAndGetArrayBounds( VARIANT Array, SAFEARRAY ** ppsa, long * pllBound, long * pluBound ) { LOG((MSP_TRACE, "VerifyAndGetArrayBounds: enter")); UINT uDims; HRESULT hr = S_OK; // // see if the variant & safearray are valid // try { if (!(V_ISARRAY(&Array))) { if ( Array.vt == VT_NULL ) { // // null is usually valid // *ppsa = NULL; LOG((MSP_INFO, "Returning NULL array")); return S_FALSE; } LOG((MSP_ERROR, "Array - not an array")); return E_INVALIDARG; } if ( Array.parray == NULL ) { // // null is usually valide // *ppsa = NULL; LOG((MSP_INFO, "Returning NULL array")); return S_FALSE; } *ppsa = V_ARRAY(&Array); uDims = SafeArrayGetDim( *ppsa ); } catch(...) { hr = E_POINTER; } if (!SUCCEEDED(hr)) { LOG((MSP_ERROR, "Array - invalid array")); return hr; } // // verify array // if ( uDims != 1 ) { if ( uDims == 0 ) { LOG((MSP_ERROR, "Array - has 0 dim")); return E_INVALIDARG; } else { LOG((MSP_WARN, "Array - has > 1 dim - will only use 1")); } } // // Get array bounds // SafeArrayGetUBound( *ppsa, 1, pluBound ); SafeArrayGetLBound( *ppsa, 1, pllBound ); LOG((MSP_TRACE, "VerifyAndGetArrayBounds: exit")); return S_OK; } ////////////////////////////////////////////////////////////////////////////// // CMDhcp::FinalContruct // // Parameters // none // // Return Values // S_OK Success // E_OUTOFMEMORY Not enough memory to create free thread marshaler // E_FAIL We are running the wrong version of dhcpcsvc.dll // // Description // This is called on construction. It creates the free threaded marshaler // and checks if the C API's DLL is the same version that we were compiled // with. ////////////////////////////////////////////////////////////////////////////// HRESULT CMDhcp::FinalConstruct(void) { LOG((MSP_TRACE, "CMDhcp::FinalConstruct - enter")); HRESULT hr = CoCreateFreeThreadedMarshaler( GetControllingUnknown(), & m_pFTM ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CMDhcp::FinalConstruct - " "failed to create FTM - exit 0x%08x", hr)); // // Now, FinalRelease will get called, and then CoCreate will return // failure. // return hr; } // Munil uses this as an IN/OUT parameter. DWORD dwVersion = MCAST_API_CURRENT_VERSION; // defined in mdhccapi.h DWORD dwCode; dwCode = McastApiStartup(&dwVersion); // dwVersion now contains the actual version of the C API, but we don't // really care what it is. if (dwCode == ERROR_SUCCESS) { m_fApiIsInitialized = TRUE; LOG((MSP_TRACE, "CMDhcp::FinalConstruct - C API version " "is >= our version - exit S_OK")); return S_OK; } else { LOG((MSP_ERROR, "CMDhcp::FinalConstruct - C API version " "is < our version - exit E_FAIL")); // // Now, FinalRelease will get called, and then CoCreate will return // failure. // return E_FAIL; } } ////////////////////////////////////////////////////////////////////////////// // // CMDhcp::FinalRelease // // Parameters // none // // Return Values // none // // Description // This is called on destruction. It releases the free threaded marshaler // and cleans up the C API instance. Note that it is also called if // FinalConstruct failed. // ////////////////////////////////////////////////////////////////////////////// void CMDhcp::FinalRelease(void) { LOG((MSP_TRACE, "CMDhcp::FinalRelease - enter")); if ( m_pFTM ) { m_pFTM->Release(); } if ( m_fApiIsInitialized ) { McastApiCleanup(); } LOG((MSP_TRACE, "CMDhcp::FinalRelease - exit")); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // IMcastAddressAllocation // // This is the main interface for the MDHCP address allocation. An // application will call CoCreateInstance on this interface to create the // MDHCP client interface object. ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // IMcastAddressAllocation::get_Scopes // // Parameters // pVariant [out] Pointer to a VARIANT that will receive an OLE-standard // Collection of available multicast scopes. Each scope // is an IDispatch pointer to an object that implements // IMcastScope. // // Return Values // S_OK Success // E_POINTER The caller passed in an invalid pointer argument // E_FAIL There are no scopes available // E_OUTOFMEMORY Not enough memory to create the required objects // other From MDhcpEnumerateScopes (win32 call) // // Description // This method is primarily for VB and other scripting languages; C++ // programmers use EnumerateScopes instead. ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP CMDhcp::get_Scopes( VARIANT * pVariant ) { LOG((MSP_TRACE, "CMDhcp::get_Scopes enter")); // Check argument. if ( IsBadWritePtr(pVariant, sizeof(VARIANT) ) ) { LOG((MSP_ERROR, "get_Scopes: invalid pointer passed in " "(%08x)", pVariant)); return E_POINTER; } DWORD i; DWORD dwScopeCount = 0; MCAST_SCOPE_ENTRY * pScopeList = NULL; HRESULT hr; BOOL fLocal; // // Grab the scopes from the C API. // hr = GetScopeList(&dwScopeCount, &pScopeList, &fLocal); if (FAILED(hr)) { LOG((MSP_ERROR, "get_Scopes: GetScopeList failed " "(hr = %08x)", hr)); return hr; } // // Now we wrap the array in COM wrappers. // IMcastScope ** ppWrappers = NULL; // this does a new into ppWrappers // as well as dwScopeCount individual object instantiations hr = CreateWrappers(dwScopeCount, pScopeList, &ppWrappers, fLocal); // At this point we've got a bunch of COM objects that contain // individual scopes, and so we no longer need the array of // scopes. Even if CreateWrappers failed we must get rid of // the array of scopes. delete pScopeList; if (FAILED(hr)) { LOG((MSP_ERROR, "get_Scopes: CreateWrappers failed " "(hr = %08x)", hr)); return hr; } // // create the collection object - see collect.h // typedef CTapiIfCollection< IMcastScope * > ScopeCollection; CComObject * p; hr = CComObject::CreateInstance( &p ); if ( (FAILED(hr)) || (p == NULL) ) { LOG((MSP_ERROR, "get_Scopes: Could not create CTapiIfCollection " "object - return %lx", hr )); for (DWORD i = 0 ; i < dwScopeCount; i++) delete ppWrappers[i]; delete ppWrappers; return hr; } // // get the Collection's IDispatch interface // IDispatch * pDisp; hr = p->_InternalQueryInterface( IID_IDispatch, (void **) &pDisp ); if ( FAILED(hr) ) { // Query interface failed so we don't know that if it addreffed // or not. LOG((MSP_ERROR, "get_Scopes: QI for IDispatch failed on " "ScopeCollection - %lx", hr )); delete p; // // PREFIXBUG 433295 - VLD // ppWrappers was allocated into CreateWrappers() method // we should deallocate it // for (DWORD i = 0 ; i < dwScopeCount; i++) delete ppWrappers[i]; delete ppWrappers; return hr; } // initialize it using an iterator -- pointers to the beginning and // the ending element plus one. hr = p->Initialize( dwScopeCount, ppWrappers, ppWrappers + dwScopeCount ); // ZoltanS fixed: // We started off by creating and calling QI on each object in // CreateWrappers. Then we passed the array of pointers to objects to // the Initialize method of the collection object. This method // called QI on each object to get each object's IDispatch pointer. // So now we are at refcount 2. We now Release() each object and get // back to refcount 1 on each object. Of course we must even do this // if the initialize failed (in that case to delete them outright). for (i = 0; i < dwScopeCount; i++) { ppWrappers[i]->Release(); } // The array of pointers must now be deleted -- we now store the // objects in the collection instead. (or nowhere if initialize failed) delete ppWrappers; if (FAILED(hr)) { // Initialize has failed -- we assume it did nothing, so we must // release all the COM objects ourselves LOG((MSP_ERROR, "get_Scopes: Could not initialize " "ScopeCollection object - return %lx", hr )); p->Release(); return hr; } // // put the IDispatch interface pointer into the variant // LOG((MSP_INFO, "placing IDispatch value %08x in variant", pDisp)); VariantInit(pVariant); pVariant->vt = VT_DISPATCH; pVariant->pdispVal = pDisp; LOG((MSP_TRACE, "CMDhcp::get_Scopes exit - return %lx", hr )); return hr; } ////////////////////////////////////////////////////////////////////////////// // IMcastAddressAllocation::EnumerateScopes // // Parameters // ppEnumMcastScope [out] Returns a pointer to a new IEnumMcastScope // object. IEnumMcastScope is a standard // enumerator interface that enumerates // IMcastScope objects. // // Return Values // S_OK Success // E_POINTER The caller passed in an invalid pointer argument // E_FAIL There are no scopes available // E_OUTOFMEMORY Not enough memory to create the required objects // other From MDhcpEnumerateScopes (win32 call) // // Description // This method is primarily for C++ programmers; VB and other scripting // languages use get_Scopes instead. ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP CMDhcp::EnumerateScopes( IEnumMcastScope ** ppEnumMcastScope ) { LOG((MSP_TRACE, "CMDhcp::EnumerateScopes enter")); if ( IsBadWritePtr(ppEnumMcastScope, sizeof(IEnumMcastScope *) ) ) { LOG((MSP_ERROR, "EnumerateScopes: bad pointer argument " "(%08x)", ppEnumMcastScope)); return E_POINTER; } DWORD dwScopeCount = 0; MCAST_SCOPE_ENTRY * pScopeList = NULL; HRESULT hr; BOOL fLocal; // // Grab the scopes from the C API. // hr = GetScopeList(&dwScopeCount, &pScopeList, &fLocal); if (FAILED(hr)) { LOG((MSP_ERROR, "EnumerateScopes: GetScopeList failed " "(hr = %08x)", hr)); return hr; } // // Now we wrap the array in COM wrappers. // IMcastScope ** ppWrappers = NULL; // this does a new into ppWrappers hr = CreateWrappers(dwScopeCount, pScopeList, &ppWrappers, fLocal); // At this point we've got a bunch of COM objects that contain // individual scopes, and so we no longer need the array of // scopes. Even if CreateWrappers failed we must get rid of // the array of scopes. delete pScopeList; if (FAILED(hr)) { LOG((MSP_ERROR, "EnumerateScopes: CreateWrappers failed " "(hr = %08x)", hr)); return hr; } // // Now we create and set up the enumerator. // typedef _CopyInterface CCopy; typedef CSafeComEnum CEnumerator; CComObject *pEnum = NULL; hr = CComObject::CreateInstance(&pEnum); if ((FAILED(hr)) || (pEnum == NULL)) { LOG((MSP_ERROR, "Couldn't create enumerator object: %08x", hr)); delete ppWrappers; return hr; } // Get the IEnumMcastScope interface. hr = pEnum->_InternalQueryInterface( IID_IEnumMcastScope, (void **)ppEnumMcastScope ); if (FAILED(hr)) { LOG((MSP_ERROR, "QI on enumerator object failed: %08x", hr)); delete ppWrappers; delete pEnum; return hr; } // This takes ownership of the wrapper list so we will no longer // delete the wrapper list if this succeeds. hr = pEnum->Init(ppWrappers, ppWrappers + dwScopeCount, NULL, AtlFlagTakeOwnership); if (FAILED(hr)) { LOG((MSP_ERROR, "Init enumerator object failed: %08x", hr)); delete ppWrappers; pEnum->Release(); return hr; } LOG((MSP_TRACE, "CMDhcp::EnumerateScopes exit")); return S_OK; } ////////////////////////////////////////////////////////////////////////////// // IMcastAddressAllocation::RequestAddress // // Parameters // pScope [in] This identifies the multicast scope from which // the application wants to be given an address. // The application first calls get_Scopes or // EnumerateScopes to obtain a list of available // scopes. // LeaseStartTime [in] Requested time for the lease on these addresses // to start / begin. The start time that is // actually granted may be different. // LeaseStopTime [in] Requested time for the lease on these addresses // to stop / end. The stop time that is actually // granted may be different. // NumAddresses [in] The number of addresses requested. Fewer // addresses may actually be granted. NOTE: // although these COM interfaces and their // implementation support allocation of multiple // addresses at a time, this is not currently // supported by the underlying Win32 calls. You // may need to use a loop instead. // ppLeaseResponse [out] Pointer to an interface pointer that will be set // to point to a new IMcastLeaseInfo object. This // interface can then be used to discover the // actual attributes of the granted lease. See // below for a description of IMcastScope. // // Return Values // S_OK Success // E_POINTER The caller passed in an invalid pointer argument // E_OUTOFMEMORY Not enough memory to create the required objects // E_INVALIDARG Requested too many addresses, format conversion // failed for the start time or stop time, or the stop // time is less than the start time // other From MdhcpRequestAddress (win32 call) // // Description // Call this method to obtain a new lease for one or more multicast // addresses. You will first need to call EnumerateScopes or get_Scopes, // as well as CreateMDhcpRequestID. ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP CMDhcp::RequestAddress(IMcastScope * pScope, DATE LeaseStartTime, DATE LeaseStopTime, long NumAddresses, IMcastLeaseInfo ** ppLeaseResponse) { LOG((MSP_TRACE, "CMDhcp::RequestAddress enter: asking for %d addresses", NumAddresses)); if ( IsBadReadPtr( pScope, sizeof(IMcastScope) ) ) { LOG((MSP_ERROR, "CMDhcp::RequestAddress - " "bad scope pointer - exit E_POINTER")); return E_POINTER; } // no need to check ppLeaseResponse -- WrapMDhcpLeaseInfo handles it MCAST_CLIENT_UID requestID; MCAST_SCOPE_CTX scopeCtx; MCAST_LEASE_INFO * pLeaseRequest; HRESULT hr; BOOL fLocal; long lTtl; // Munge input arguments into three structs for passing to the C API. // pLeaseRequest and requestID->ClientUID are allocated. We must delete them when // we're done. hr = PrepareArgumentsRequest(pScope, // goes into scopeCtx LeaseStartTime, // goes into leaseRequest LeaseStopTime, // goes into leaseRequest NumAddresses, // goes into leaseRequest &requestID, // we generate it &scopeCtx, &pLeaseRequest, &fLocal, &lTtl ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CMDHcp::RequestAddress - " "PrepareArgumentsRequest failed - exit 0x%08x", hr)); return hr; } MCAST_LEASE_INFO * pLeaseResponse = (MCAST_LEASE_INFO *) new BYTE [ sizeof(MCAST_LEASE_INFO) + sizeof(DWORD) * NumAddresses ]; if (pLeaseResponse == NULL) { LOG((MSP_ERROR, "RequestAddress: out of memory in response alloc")); delete requestID.ClientUID; delete pLeaseRequest; return E_OUTOFMEMORY; } DWORD dwCode; dwCode = LocalRequestAddress(fLocal, &requestID, &scopeCtx, pLeaseRequest, pLeaseResponse); // No matter what, we no longer need this. delete pLeaseRequest; if (dwCode != ERROR_SUCCESS) { LOG((MSP_ERROR, "RequestAddress: C API call failed " "(code = %d)", dwCode)); delete requestID.ClientUID; delete pLeaseResponse; return HRESULT_FROM_ERROR_CODE(dwCode); } // Wrap the lease response, along with the requestID, in an interface // and return it. // The wrapper assumes ownership of the lease structure and // requestID.clientuid. hr = WrapMDhcpLeaseInfo(TRUE, lTtl, fLocal, pLeaseResponse, &requestID, ppLeaseResponse); if ( FAILED(hr) ) { LOG((MSP_ERROR, "RequestAddress: WrapMDhcpLeaseInfo failed " "(hr = %08x)", hr)); delete pLeaseResponse; delete requestID.ClientUID; return hr; } return S_OK; } ////////////////////////////////////////////////////////////////////////////// // IMcastAddressAllocation::RenewAddress // // Parameters // pRenewRequest [in] Pointer to an IMcastLeaseInfo object specifying // the attributes of the requested renewal, such // as which address(es) to renew. This is // obtained by calling CreateLeaseInfo. // ppRenewResponse [out] Pointer to an interface pointer that will be set // to point to a new IMcastLeaseInfo object. This // interface can then be used to discover the // attributes of the renewed lease. See below for // a description of IMcastScope. // // Return Values // S_OK Success // E_OUTOFMEMORY Not enough memory to create the required objects // E_POINTER The caller passed in an invalid pointer argument // E_INVALIDARG Start time is greater than stop time // other From MdhcpRenewAddress (win32 call) // // Description // To renew a lease, call CreateLeaseInfo to specify the parameters of // the renewal request, and then call this method to make the request. ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP CMDhcp::RenewAddress( long lReserved, // unused IMcastLeaseInfo * pRenewRequest, IMcastLeaseInfo ** ppRenewResponse ) { LOG((MSP_TRACE, "CMDhcp::RenewAddress enter")); // no need to check pRequestID or pRenewRequest -- // PrepareArgumentsNonRequest handles that. // ppRenewResponse check handled by WrapMDhcpLeaseInfo MCAST_CLIENT_UID requestID; MCAST_LEASE_INFO * pRenewRequestStruct; HRESULT hr; BOOL fLocal; BOOL fGotTtl; long lTtl; // Munge input arguments into three structs for passing to the C API. // pLeaseRequest and requestID->ClientUID are allocated. We must delete them when // we're done. hr = PrepareArgumentsNonRequest(pRenewRequest, &requestID, &pRenewRequestStruct, &fLocal, &fGotTtl, &lTtl); if ( FAILED(hr) ) { LOG((MSP_ERROR, "RenewAddress: PrepareArgumentsNonRequest failed " "(hr = %08x)", hr)); return hr; } // // Check that the start time is less than the stop time // if ( pRenewRequestStruct->LeaseStartTime > pRenewRequestStruct->LeaseEndTime ) { LOG((MSP_ERROR, "PrepareArgumentsNonRequest - " "start time %d is greater than stop time %d - exit E_INVALIDARG", pRenewRequestStruct->LeaseStartTime, pRenewRequestStruct->LeaseEndTime)); delete requestID.ClientUID; delete pRenewRequestStruct; return E_INVALIDARG; } MCAST_LEASE_INFO * pRenewResponse = (MCAST_LEASE_INFO *) new BYTE [ sizeof(MCAST_LEASE_INFO) + sizeof(DWORD) * pRenewRequestStruct->AddrCount ]; if ( pRenewResponse == NULL ) { LOG((MSP_ERROR, "RenewAddress: out of memory in response alloc")); delete requestID.ClientUID; delete pRenewRequestStruct; return E_OUTOFMEMORY; } DWORD dwCode = LocalRenewAddress(fLocal, &requestID, pRenewRequestStruct, pRenewResponse); // // We have performed the renew request so we no longer need the struct // for the request, even if the request failed. // delete pRenewRequestStruct; if ( dwCode != ERROR_SUCCESS ) { LOG((MSP_ERROR, "RenewAddress: C API call failed " "(code = %d)", dwCode)); delete requestID.ClientUID; delete pRenewResponse; return HRESULT_FROM_ERROR_CODE(dwCode); } // // Wrap pRenewResponse and the requestID in an interface and return it. // the wrapper takes ownership of the requestID.clientUID and the // response struct // hr = WrapMDhcpLeaseInfo(fGotTtl, lTtl, fLocal, pRenewResponse, &requestID, ppRenewResponse); if ( FAILED(hr) ) { LOG((MSP_ERROR, "RenewAddress: WrapMDhcpLeaseInfo failed " "(hr = %08x)", hr)); delete requestID.ClientUID; delete pRenewResponse; return hr; } LOG((MSP_TRACE, "CMDhcp::RenewAddress exit")); return S_OK; } ////////////////////////////////////////////////////////////////////////////// // IMcastAddressAllocation::ReleaseAddress // // Parameters // pReleaseRequest [in] Pointer to an IMcastLeaseInfo object specifying // the which address(es) to release. This is // returned from a previous RequestAddress call or // obtained by calling CreateLeaseInfo. // // Return Values // S_OK Success // E_POINTER The caller passed in an invalid pointer argument // E_OUTOFMEMORY Not enough memory to make the request // other From MdhcpReleaseAddress (win32 call) // // Description // Use this method to release a lease that was obtained previously. ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP CMDhcp::ReleaseAddress( IMcastLeaseInfo * pReleaseRequest ) { LOG((MSP_TRACE, "CMDhcp::ReleaseAddress enter")); // no need to check pReleaseRequest -- // PrepareArgumentsNonRequest handles that. MCAST_CLIENT_UID requestID; MCAST_LEASE_INFO * pReleaseRequestStruct; HRESULT hr; BOOL fLocal; BOOL fGotTtl; // unused after call long lTtl; // unused after call hr = PrepareArgumentsNonRequest(pReleaseRequest, &requestID, &pReleaseRequestStruct, &fLocal, &fGotTtl, &lTtl ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "ReleaseAddress: PrepareArgumentsNonRequest failed " "(hr = %08x)", hr)); return hr; } DWORD dwCode; dwCode = LocalReleaseAddress(fLocal, &requestID, pReleaseRequestStruct); // // These were allocated by PrepareArgumentsNonRequest and there is no one // to own them now -- we delete them. This is true even if the // LocalReleaseAddress call failed. // delete pReleaseRequestStruct; delete requestID.ClientUID; if ( dwCode != ERROR_SUCCESS ) { LOG((MSP_ERROR, "ReleaseAddress: C API call failed " "(code = %d)", dwCode)); return HRESULT_FROM_ERROR_CODE(dwCode); } LOG((MSP_TRACE, "CMDhcp::ReleaseAddress exit")); return S_OK; } ////////////////////////////////////////////////////////////////////////////// // IMcastAddressAllocation::CreateLeaseInfo // // Parameters // LeaseStartTime [in] The start time of the lease. // LeaseStopTime [in] The stop time of the lease. // dwNumAddresses [in] The number of addresses associated with the // lease. // ppAddresses [in] An array of LPWSTRs of size dwNumAddresses. Each // LPWSTR (Unicode string pointer) is an IPv4 // address in "dot-quad" notation; e.g. // "123.234.12.17". // pRequestID [in] An LPWSTR (Unicode string pointer) specifying // the request ID for the original request. // pServerAddress [in] An LPWSTR (Unicode string pointer) specifying // the address of the server that granted the // original request. This address is an IPv4 // address in "dot quad" notation; e.g. // "123.234.12.17". // ppReleaseRequest [out] Returns a pointer to the IMcastLeaseInfo // interface on the newly created lease // information object. // // Return Values // S_OK Success // E_POINTER The caller passed in an invalid pointer argument // E_OUTOFMEMORY Not enough memory to create the required objects // E_INVALIDARG An error occured during the date format conversion // // Description // Use this method to create a lease information object for a subsequent // RenewAddress or ReleaseAddress call. This method is primarily for C++ // programmers; VB and other scripting languages use // CreateLeaseInfoFromVariant instead. // The dwNumAddresses, ppAddresses, pRequestID, and pServerAddress // parameters are normally obtained by calling the corresponding // IMcastLeaseInfo methods on the lease info object corresponding to the // original request. These values should be saved in persistent storage // between executions of the application program. If you are renewing or // releasing a lease that was requested during the same run of the // application, you have no reason to use CreateLeaseInfo; just pass the // existing IMcastLeaseInfo pointer to RenewAddress or ReleaseAddress. ////////////////////////////////////////////////////////////////////////////// #include STDMETHODIMP CMDhcp::CreateLeaseInfo( DATE LeaseStartTime, DATE LeaseStopTime, DWORD dwNumAddresses, LPWSTR * ppAddresses, LPWSTR pRequestID, LPWSTR pServerAddress, IMcastLeaseInfo ** ppReleaseRequest ) { LOG((MSP_TRACE, "CMDhcp::CreateLeaseInfo enter")); if ( IsBadWritePtr(ppReleaseRequest, sizeof(IMcastLeaseInfo *) ) ) { LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfo - " "invalid lease return pointer: 0x%08x - exit E_POINTER", ppReleaseRequest)); return E_POINTER; } if ( IsBadStringPtr(pRequestID, (UINT) -1 ) ) { LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfo - " "invalid RequestID pointer: 0x%08x - exit E_POINTER", pRequestID)); return E_POINTER; } if ( ( dwNumAddresses < 1 ) || ( dwNumAddresses > USHRT_MAX ) ) { LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfo - " "invalid number of addresses: %d - exit E_INVALIDARG", dwNumAddresses)); return E_INVALIDARG; } if (IsBadReadPtr(ppAddresses, sizeof(LPWSTR) * dwNumAddresses) ) { LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfo - " "invalid addresses array pointer: 0x%08x - exit E_POINTER", ppAddresses)); return E_POINTER; } if ( IsBadStringPtr(pServerAddress, (UINT) -1 ) ) { LOG((MSP_ERROR, "CreateLeaseInfo: invalid Server Address pointer: %08x", pRequestID)); return E_POINTER; } HRESULT hr; // create the com object. CComObject * pMDhcpLeaseInfo; hr = CComObject::CreateInstance(&pMDhcpLeaseInfo); if ( (FAILED(hr)) || (pMDhcpLeaseInfo == NULL) ) { LOG((MSP_ERROR, "CreateLeaseInfo: can't create MDhcpLeaseInfo Object.")); return hr; } // Get the IMcastLeaseInfo interface. hr = pMDhcpLeaseInfo->_InternalQueryInterface( IID_IMcastLeaseInfo, (void **)ppReleaseRequest ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CreateLeaseInfo: QueryInterface failed: %x", hr)); delete pMDhcpLeaseInfo; return hr; } // Fill in the object with the stuff the user wanted. hr = pMDhcpLeaseInfo->Initialize(LeaseStartTime, LeaseStopTime, dwNumAddresses, ppAddresses, pRequestID, pServerAddress); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CreateLeaseInfo: Initialize failed: %x", hr)); delete pMDhcpLeaseInfo; return hr; } LOG((MSP_TRACE, "CMDhcp::CreateLeaseInfo exit")); return S_OK; } ////////////////////////////////////////////////////////////////////////////// // IMcastAddressAllocation::CreateLeaseInfoFromVariant // // Parameters // LeaseStartTime [in] The start time of the lease. // LeaseStopTime [in] The stop time of the lease. // vAddresses [in] A VARIANT containing a SafeArray of BSTRs. Each // BSTR (size-tagged Unicode string pointer) is // an IPv4 address in "dot-quad" notation; e.g. // "123.234.12.17". // pRequestID [in] A BSTR (size-tagged Unicode string pointer) // specifying the request ID for the original // request. // pServerAddress [in] A BSTR (size-tagged Unicode string pointer) // specifying the address of the server that // granted the original request. This address is // an IPv4 address in "dot quad" notation; e.g. // "123.234.12.17". // ppReleaseRequest [out] Returns a pointer to the IMcastLeaseInfo // interface on the newly created lease // information object. // // Return Values // S_OK Success // E_POINTER The caller passed in an invalid pointer argument // E_OUTOFMEMORY Not enough memory to create the required objects // E_INVALIDARG An error occured during the date format conversion // // Description // Use this method to create a lease information object for a subsequent // RenewAddress or ReleaseAddress call. This method is primarily for VB // and other scripting languages; C++ programmers should use // CreateLeaseInfo instead. // The dwNumAddresses, ppAddresses, pRequestID, and pServerAddress // parameters are normally obtained by calling the corresponding // IMcastLeaseInfo methods on the lease info object corresponding to the // original request. These values should be saved in persistent storage // between executions of the application program. If you are renewing or // releasing a lease that was requested during the same run of the // application, you have no reason to use CreateLeaseInfoFromVariant; just // pass the existing IMcastLeaseInfo pointer to RenewAddress or // ReleaseAddress. ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP CMDhcp::CreateLeaseInfoFromVariant( DATE LeaseStartTime, DATE LeaseStopTime, VARIANT vAddresses, BSTR pRequestID, BSTR pServerAddress, IMcastLeaseInfo ** ppReleaseRequest ) { LOG((MSP_TRACE, "CMDhcp::CreateLeaseInfoFromVariant enter")); // We will check the pointers in CreateLeaseInfo. HRESULT hr; // Get from the variant: DWORD dwNumAddresses; LPWSTR * ppAddresses; SAFEARRAY * psaAddresses = NULL; // SafeArray with the addresses long lLowerBound = 0; // lower bound of safearray long lUpperBound = 0; // upper bound of safearray hr = VerifyAndGetArrayBounds( vAddresses, &psaAddresses, &lLowerBound, &lUpperBound); if (FAILED(hr)) { LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfoFromVariant: invalid " "VARIANT")); return E_INVALIDARG; } // This is how many addresses we *expect* (may have fewer). long lAddrCount = lUpperBound - lLowerBound + 1; if (lAddrCount < 1) { LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfoFromVariant: too few " "addresses (check #1) (%d)", lAddrCount)); return E_INVALIDARG; } // We allocate as many as we are told to expect, but some of this // space may end up getting "wasted" if there are fewer valid ones // after all. ppAddresses = new LPWSTR[lAddrCount]; if (ppAddresses == NULL) { LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfoFromVariant: " "out of memory in array allocation")); return E_OUTOFMEMORY; } long lCurrSrc; // the current element in the safearray (source) long lCurrDest = 0; // the current element in the struct's array (destination) // Get the addresses from the SafeArray and put them in our array. for (lCurrSrc = lLowerBound; lCurrSrc <= lUpperBound; lCurrSrc++) { hr = SafeArrayGetElement( psaAddresses, &lCurrSrc, & ( ppAddresses[lCurrDest] ) ); if ( FAILED(hr) ) { LOG((MSP_INFO, "CMDhcp::CreateLeaseInfoFromVariant: " "failed to get safearray element %d" " - skipping (array range %d-%d)", lCurrSrc, lLowerBound, lUpperBound)); } else if ( ppAddresses[lCurrDest] == 0 ) { LOG((MSP_INFO, "CMDhcp::CreateLeaseInfoFromVariant: " "got ZERO address from safearray " "element %d - skipping (array range %d-%d)", lCurrSrc, lLowerBound, lUpperBound)); } else { // We got an element. lCurrDest++; } } // note the number of addresses we actually placed in the array dwNumAddresses = (DWORD) lCurrDest; if (dwNumAddresses < 1) { LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfoFromVariant: " "too few addresses (check #2)" "(%d)", lAddrCount)); delete ppAddresses; return E_INVALIDARG; } hr = CreateLeaseInfo(LeaseStartTime, LeaseStopTime, dwNumAddresses, ppAddresses, pRequestID, pServerAddress, ppReleaseRequest ); // No matter what, we no longer need this. delete ppAddresses; if (FAILED(hr)) { LOG((MSP_TRACE, "CMDhcp::CreateLeaseInfoFromVariant : " "CreateLeaseInfo returned %08x", hr)); return hr; } LOG((MSP_TRACE, "CMDhcp::CreateLeaseInfoFromVariant exit")); return S_OK; } // eof