Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1344 lines
41 KiB

  1. /*
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. Module Name:
  4. lease.cpp
  5. Abstract:
  6. Implementation of CMDhcpLeaseInfo.
  7. Author:
  8. */
  9. #include "stdafx.h"
  10. #include "mdhcp.h"
  11. #include "lease.h"
  12. #include "collect.h"
  13. #include <winsock2.h>
  14. #include <time.h>
  15. /////////////////////////////////////////////////////////////////////////////
  16. // Convert an OLE DATE (64-bit floating point) to the time format used in
  17. // an MDHCP lease info structure (currently time_t).
  18. HRESULT DateToLeaseTime(DATE date, LONG * pLeaseTime)
  19. {
  20. //
  21. // Note:
  22. //
  23. // pLeaseTime is intentionally a pointer to LONG instead of a pointer to
  24. // time_t. This is because this function is used to convert 32-bit time
  25. // values that are sent over the wire.
  26. //
  27. LOG((MSP_TRACE, "DateToLeaseTime: enter"));
  28. if ( IsBadWritePtr(pLeaseTime, sizeof(LONG)) )
  29. {
  30. LOG((MSP_ERROR, "DateToLeaseTime: invalid pLeaseTime pointer "
  31. "(ptr = %p)", pLeaseTime));
  32. return E_POINTER;
  33. }
  34. //
  35. // Step one: convert variant time to system time.
  36. //
  37. SYSTEMTIME systemTime;
  38. time_t scratchTime;
  39. // This is TRUE or FALE, not a Win32 result code.
  40. INT iCode;
  41. iCode = VariantTimeToSystemTime(date, &systemTime);
  42. if (iCode == 0)
  43. {
  44. LOG((MSP_ERROR, "DateToLeaseTime: VariantTimeToSystemTime call failed "
  45. "(code = %d)", iCode));
  46. return E_INVALIDARG;
  47. }
  48. //
  49. // Step two: Convert system time to time_t.
  50. //
  51. tm Tm;
  52. Tm.tm_year = (int) systemTime.wYear - 1900;
  53. Tm.tm_mon = (int) systemTime.wMonth - 1;
  54. Tm.tm_mday = (int) systemTime.wDay;
  55. Tm.tm_wday = (int) systemTime.wDayOfWeek;
  56. Tm.tm_hour = (int) systemTime.wHour;
  57. Tm.tm_min = (int) systemTime.wMinute;
  58. Tm.tm_sec = (int) systemTime.wSecond;
  59. Tm.tm_isdst = -1; // ask win32 to compute DST for us (crucial!)
  60. // not filled in: Tm.tm_yday;
  61. scratchTime = mktime(&Tm);
  62. if ( scratchTime == -1 )
  63. {
  64. LOG((MSP_ERROR, "DateToLeaseTime: mktime call failed "));
  65. return E_INVALIDARG;
  66. }
  67. //
  68. // Now truncate scratchTime and store in out param. This will be
  69. // truncated in 2038.
  70. //
  71. *pLeaseTime = (LONG)scratchTime;
  72. LOG((MSP_TRACE, "DateToLeaseTime: exit"));
  73. return S_OK;
  74. }
  75. /////////////////////////////////////////////////////////////////////////////
  76. // Convert to an OLE DATE structure from a time_t (which is what the MDHCP
  77. // lease info structure now uses).
  78. HRESULT LeaseTimeToDate(time_t leaseTime, DATE * pDate)
  79. {
  80. LOG((MSP_TRACE, "LeaseTimeToDate: enter"));
  81. if ( IsBadWritePtr(pDate, sizeof(DATE)) )
  82. {
  83. LOG((MSP_ERROR, "LeaseTimeToDate: invalid pDate pointer "
  84. "(ptr = %08x)", pDate));
  85. return E_POINTER;
  86. }
  87. //
  88. // Step one: Convert the time_t to a system time.
  89. //
  90. // get the tm struct for this time value
  91. tm * pTm = localtime(&leaseTime);
  92. if (pTm == NULL)
  93. {
  94. LOG((MSP_ERROR, "LeaseTimeToDate: localtime call failed - "
  95. "exit E_INVALIDARG"));
  96. return E_INVALIDARG;
  97. }
  98. SYSTEMTIME systemTime;
  99. // set the ref parameters to the tm struct values
  100. systemTime.wYear = (WORD) pTm->tm_year + 1900; // years since 1900
  101. systemTime.wMonth = (WORD) pTm->tm_mon + 1; // months SINCE january (0,11)
  102. systemTime.wDay = (WORD) pTm->tm_mday;
  103. systemTime.wDayOfWeek = (WORD) pTm->tm_wday;
  104. systemTime.wHour = (WORD) pTm->tm_hour;
  105. systemTime.wMinute = (WORD) pTm->tm_min;
  106. systemTime.wSecond = (WORD) pTm->tm_sec;
  107. systemTime.wMilliseconds = 0;
  108. //
  109. // Step 2: Convert the system time to a variant time.
  110. //
  111. int iCode = SystemTimeToVariantTime(&systemTime, pDate);
  112. if (iCode == 0)
  113. {
  114. LOG((MSP_ERROR, "LeaseTimeToDate: SystemToVariantTime call failed "
  115. "(code = %d)", iCode));
  116. return E_INVALIDARG;
  117. }
  118. LOG((MSP_TRACE, "LeaseTimeToDate: exit"));
  119. return S_OK;
  120. }
  121. /////////////////////////////////////////////////////////////////////////////
  122. /////////////////////////////////////////////////////////////////////////////
  123. // Now for the CMDhcpLeaseInfo class.
  124. /////////////////////////////////////////////////////////////////////////////
  125. /////////////////////////////////////////////////////////////////////////////
  126. /////////////////////////////////////////////////////////////////////////////
  127. // Constructors.
  128. CMDhcpLeaseInfo::CMDhcpLeaseInfo(void) :
  129. m_pLease(NULL),
  130. m_fGotTtl(FALSE),
  131. m_lTtl(0),
  132. m_pFTM(NULL),
  133. m_fLocal(FALSE)
  134. {
  135. LOG((MSP_TRACE, "CMDhcpLeaseInfo constructor: enter"));
  136. m_RequestID.ClientUID = NULL;
  137. m_RequestID.ClientUIDLength = 0;
  138. LOG((MSP_TRACE, "CMDhcpLeaseInfo constructor: exit"));
  139. }
  140. HRESULT CMDhcpLeaseInfo::FinalConstruct(void)
  141. {
  142. LOG((MSP_TRACE, "CMDhcpLeaseInfo::FinalConstruct: enter"));
  143. HRESULT hr = CoCreateFreeThreadedMarshaler( GetControllingUnknown(),
  144. & m_pFTM );
  145. if ( FAILED(hr) )
  146. {
  147. LOG((MSP_ERROR, "CMDhcpLeaseInfo::FinalConstruct: "
  148. "create FTM failed 0x%08x", hr));
  149. return hr;
  150. }
  151. hr = m_CriticalSection.Initialize();
  152. if( FAILED(hr) )
  153. {
  154. LOG((MSP_ERROR, "CMDhcpLeaseInfo::FinalConstruct: "
  155. "critical section initialize failed 0x%08x", hr));
  156. return hr;
  157. }
  158. LOG((MSP_TRACE, "CMDhcpLeaseInfo::FinalConstruct: exit S_OK"));
  159. return S_OK;
  160. }
  161. /////////////////////////////////////////////////////////////////////////////
  162. // Destructors.
  163. void CMDhcpLeaseInfo::FinalRelease(void)
  164. {
  165. LOG((MSP_TRACE, "CMDhcpLeaseInfo::FinalRelease: enter"));
  166. delete m_pLease;
  167. delete m_RequestID.ClientUID;
  168. if ( m_pFTM )
  169. {
  170. m_pFTM->Release();
  171. }
  172. LOG((MSP_TRACE, "CMDhcpLeaseInfo::FinalRelease: exit"));
  173. }
  174. CMDhcpLeaseInfo::~CMDhcpLeaseInfo(void)
  175. {
  176. LOG((MSP_TRACE, "CMDhcpLeaseInfo destructor: enter"));
  177. LOG((MSP_TRACE, "CMDhcpLeaseInfo destructor: exit"));
  178. }
  179. /////////////////////////////////////////////////////////////////////////////
  180. // The simplest way to initialize our structure. This happens when the C API
  181. // returns a pointer to an MCAST_LEASE_INFO and we want to return our wrapped
  182. // interface to the user of our COM API, along with the MCAST_CLIENT_UID
  183. // (request ID) that was used.
  184. //
  185. // The MCAST_LEASE_INFO that we're pointing to was created by a COM method in
  186. // IMcastAddressAllocation... it was created via "new" according to the number of addresses
  187. // we expected to get back. We now take ownership of it and it will be deleted
  188. // when we are destroyed. Since this method is not accessible via COM, we trust
  189. // our own IMcastAddressAllocation implementation to do the allocation for us. However we still
  190. // do asserts for debug builds.
  191. //
  192. // We also take ownership of the RequestID; we are responsible for deleting it
  193. // upon our destruction.
  194. //
  195. HRESULT CMDhcpLeaseInfo::Wrap(
  196. MCAST_LEASE_INFO * pLease,
  197. MCAST_CLIENT_UID * pRequestID,
  198. BOOL fGotTtl,
  199. long lTtl
  200. )
  201. {
  202. LOG((MSP_TRACE, "CMDhcpLeaseInfo::Wrap: enter"));
  203. _ASSERTE( ! IsBadReadPtr(pLease, sizeof(MCAST_LEASE_INFO)) );
  204. _ASSERTE( ! IsBadReadPtr(pLease, sizeof(MCAST_LEASE_INFO) +
  205. (pLease->AddrCount - 1) * sizeof(DWORD)) );
  206. _ASSERTE( ! IsBadWritePtr(pLease, sizeof(MCAST_LEASE_INFO) +
  207. (pLease->AddrCount - 1) * sizeof(DWORD)) );
  208. _ASSERTE( ! IsBadReadPtr(pRequestID, sizeof(MCAST_CLIENT_UID)) );
  209. _ASSERTE( pRequestID->ClientUIDLength == MCAST_CLIENT_ID_LEN );
  210. _ASSERTE( ! IsBadReadPtr(pRequestID->ClientUID,
  211. pRequestID->ClientUIDLength) );
  212. //
  213. // Takes ownership of the following dynamically-allocated items:
  214. // * lease info
  215. // * requestId.clientUID.
  216. //
  217. m_fGotTtl = fGotTtl;
  218. m_lTtl = lTtl;
  219. m_pLease = pLease;
  220. m_RequestID = *pRequestID;
  221. LOG((MSP_TRACE, "CMDhcpLeaseInfo::Wrap: exit"));
  222. return S_OK;
  223. }
  224. /////////////////////////////////////////////////////////////////////////////
  225. // Allocate a variable-sized MCAST_LEASE_INFO structure, copy our structure
  226. // into it, and return a pointer to the new structure.
  227. HRESULT CMDhcpLeaseInfo::GetStruct(MCAST_LEASE_INFO ** ppLease)
  228. {
  229. LOG((MSP_TRACE, "CMDhcpLeaseInfo::GetStruct: enter"));
  230. if ( IsBadWritePtr(ppLease, sizeof(MCAST_LEASE_INFO *)) )
  231. {
  232. LOG((MSP_ERROR, "CMDhcpLeaseInfo::GetStruct: bad pointer passed in"));
  233. return E_POINTER;
  234. }
  235. //
  236. // Compute the size of the existing structure.
  237. //
  238. DWORD dwSize = sizeof(MCAST_LEASE_INFO) +
  239. m_pLease->AddrCount * sizeof(DWORD);
  240. //
  241. // New an appropriately-sized struct. The caller will delete it after
  242. // the API call.
  243. //
  244. (*ppLease) = (MCAST_LEASE_INFO *) new BYTE[dwSize];
  245. if ((*ppLease) == NULL)
  246. {
  247. LOG((MSP_ERROR, "GetStruct: out of memory in "
  248. "MCAST_LEASE_INFO allocation"));
  249. return E_OUTOFMEMORY;
  250. }
  251. //
  252. // Copy to the new structure.
  253. //
  254. CopyMemory(*ppLease, m_pLease, dwSize);
  255. LOG((MSP_TRACE, "CMDhcpLeaseInfo::GetStruct: exit"));
  256. return S_OK;
  257. }
  258. /////////////////////////////////////////////////////////////////////////////
  259. // Initialize the fields of the structure. This is the case where the user
  260. // has called IMcastAddressAllocation::CreateLeaseInfo and intends to do a renew or release
  261. // right after this.
  262. //
  263. // Note: the addresses are in NETWORK byte order and remain so.
  264. //
  265. HRESULT CMDhcpLeaseInfo::Initialize(DATE LeaseStartTime,
  266. DATE LeaseStopTime,
  267. DWORD dwNumAddresses,
  268. LPWSTR * ppAddresses,
  269. LPWSTR pRequestID,
  270. LPWSTR pServerAddress)
  271. {
  272. LOG((MSP_TRACE, "CMDhcpLeaseInfo::Initialize (create): enter"));
  273. // For ATL string type conversion macros.
  274. USES_CONVERSION;
  275. // These should already have been checked by CreateLeaseInfo
  276. _ASSERTE( dwNumAddresses >= 1 );
  277. _ASSERTE( dwNumAddresses <= USHRT_MAX );
  278. _ASSERTE( ! IsBadReadPtr (ppAddresses, sizeof(LPWSTR) * dwNumAddresses) );
  279. _ASSERTE( ! IsBadStringPtr(pRequestID, (UINT) -1 ) );
  280. _ASSERTE( ! IsBadStringPtr(pServerAddress, (UINT) -1 ) );
  281. // Let's check all the addresses here before we get too deep into this...
  282. DWORD i;
  283. for ( i = 0; i < dwNumAddresses; i++ )
  284. {
  285. if ( IsBadStringPtr(ppAddresses[i], (UINT) -1 ) )
  286. {
  287. LOG((MSP_ERROR, "CMDhcpLeaseInfo::Initialize (create): bad "
  288. "string pointer found"));
  289. return E_POINTER;
  290. }
  291. }
  292. // Set the request ID via a private method. No need to check pRequestID --
  293. // this checks it for us. (It's no problem that it's not really a BSTR,
  294. // because we don't use the size tag anywhere.)
  295. HRESULT hr = put_RequestID(pRequestID);
  296. if ( FAILED(hr) )
  297. {
  298. LOG((MSP_ERROR, "Initialize: failed to set RequestID"));
  299. return E_INVALIDARG;
  300. }
  301. // Allocate space for as many addresses as needed.
  302. // NOTE: If we fail from here on, we DO NOT need to delete this buffer,
  303. // because returning a failure from this function will cause the whole
  304. // CMDhcpLeaseInfo to be deleted, which will cause m_pLease to be deleted.
  305. m_pLease = (MCAST_LEASE_INFO *) new BYTE
  306. [ sizeof(MCAST_LEASE_INFO) + sizeof(DWORD) * dwNumAddresses ];
  307. if (m_pLease == NULL)
  308. {
  309. LOG((MSP_ERROR, "Initialize: out of memory in struct allocation"));
  310. return E_OUTOFMEMORY;
  311. }
  312. // note the number of addresses in the structure
  313. m_pLease->AddrCount = (WORD) dwNumAddresses;
  314. m_pLease->pAddrBuf = ( (PBYTE) m_pLease ) + sizeof( MCAST_LEASE_INFO );
  315. // note: assumes ipv4
  316. DWORD * pdwAddresses = (DWORD *) m_pLease->pAddrBuf;
  317. // Get the addresses from the array and put them in our structure.
  318. for (i = 0; i < dwNumAddresses; i++)
  319. {
  320. // we already checked the BSTR
  321. pdwAddresses[i] = inet_addr(W2A(ppAddresses[i]));
  322. }
  323. hr = DateToLeaseTime(LeaseStartTime, &(m_pLease->LeaseStartTime));
  324. if (FAILED(hr)) return hr;
  325. hr = DateToLeaseTime(LeaseStopTime, &(m_pLease->LeaseEndTime));
  326. if (FAILED(hr)) return hr;
  327. //
  328. // We don't know the TTL. Leave it alone.
  329. //
  330. //
  331. // Set the server address. If the server addess is 127.0.0.1
  332. // (in net byte order) then mark this as a local lease.
  333. //
  334. m_pLease->ServerAddress.IpAddrV4 = inet_addr(W2A(pServerAddress));
  335. SetLocal( m_pLease->ServerAddress.IpAddrV4 == 0x0100007f );
  336. //
  337. // All done...
  338. //
  339. LOG((MSP_TRACE, "CMDhcpLeaseInfo::Initialize (create): exit"));
  340. return S_OK;
  341. }
  342. /////////////////////////////////////////////////////////////////////////////
  343. // This is a small helper function to print an IP address to a Unicode string.
  344. // We can't use inet_ntoa because we need Unicode.
  345. static inline void ipAddressToStringW(WCHAR * wszDest, DWORD dwAddress)
  346. {
  347. // The IP address is always stored in NETWORK byte order.
  348. // So we need to take something like 0x0100007f and produce a string like
  349. // "127.0.0.1".
  350. wsprintf(wszDest, L"%d.%d.%d.%d",
  351. dwAddress & 0xff,
  352. (dwAddress >> 8) & 0xff,
  353. (dwAddress >> 16) & 0xff,
  354. dwAddress >> 24 );
  355. }
  356. /////////////////////////////////////////////////////////////////////////////
  357. // Private helper funciton to make an array of BSTRs from our array of
  358. // DWORD addresses.
  359. //
  360. HRESULT CMDhcpLeaseInfo::MakeBstrArray(BSTR ** ppbszArray)
  361. {
  362. LOG((MSP_TRACE, "CMDhcpLeaseInfo::MakeBstrArray: enter"));
  363. if ( IsBadWritePtr(ppbszArray, sizeof(BSTR *)) )
  364. {
  365. LOG((MSP_ERROR, "MakeBstrArray: invalid pointer argument passed in"));
  366. return E_POINTER;
  367. }
  368. *ppbszArray = new BSTR[m_pLease->AddrCount];
  369. if ( (*ppbszArray) == NULL)
  370. {
  371. LOG((MSP_ERROR, "MakeBstrArray: out of memory in array allocation"));
  372. return E_OUTOFMEMORY;
  373. }
  374. WCHAR wszBuffer[100]; // quite big enough for this
  375. for (DWORD i = 0 ; i < m_pLease->AddrCount; i++)
  376. {
  377. // note: we do not support ipv6
  378. ipAddressToStringW( wszBuffer, ((DWORD *) m_pLease->pAddrBuf)[i] );
  379. (*ppbszArray)[i] = SysAllocString(wszBuffer);
  380. if ( (*ppbszArray)[i] == NULL )
  381. {
  382. LOG((MSP_ERROR, "MakeBstrArray: out of memory in string allocation"));
  383. for ( DWORD j = 0; j < i; j++ )
  384. {
  385. SysFreeString((*ppbszArray)[j]);
  386. }
  387. delete (*ppbszArray);
  388. *ppbszArray = NULL;
  389. return E_OUTOFMEMORY;
  390. }
  391. }
  392. LOG((MSP_TRACE, "CMDhcpLeaseInfo::MakeBstrArray: exit"));
  393. return S_OK;
  394. }
  395. //////////////////////////////////////////////////////////////////////////////
  396. // Private helper method
  397. // CMDhcpLeaseInfo::GetRequestIDBuffer
  398. //
  399. // Parameters
  400. // lBufferSize [in] This argument indicates the size of the buffer
  401. // pointed to by pBuffer.
  402. // pBuffer [in, out] This argument points to a buffer that the caller
  403. // has allocated, of size lBufferSize. This
  404. // buffer will be filled with a copy of the
  405. // unique identifier.
  406. //
  407. // Return Values
  408. // S_OK Success
  409. // E_POINTER The caller passed in an invalid pointer argument
  410. // E_INVALIDARG The supplied buffer is too small
  411. //
  412. // Description
  413. // Use this method to obtain a copy of the unique identifier.
  414. /////////////////////////////////////////////////////////////////////////////
  415. HRESULT CMDhcpLeaseInfo::GetRequestIDBuffer(
  416. long lBufferSize,
  417. BYTE * pBuffer
  418. )
  419. {
  420. LOG((MSP_TRACE, "CMDhcpLeaseInfo::GetRequestIDBuffer: enter"));
  421. //
  422. // Check return pointer.
  423. //
  424. if ( IsBadWritePtr(pBuffer, lBufferSize) )
  425. {
  426. LOG((MSP_ERROR, "requestID GetRequestIDBuffer: bad pointer passed in"));
  427. return E_POINTER;
  428. }
  429. //
  430. // Check that the caller has enough space to grab the entire client id.
  431. //
  432. if ( lBufferSize < MCAST_CLIENT_ID_LEN )
  433. {
  434. LOG((MSP_ERROR, "requestID GetRequestIDBuffer: specified buffer too small"));
  435. return E_INVALIDARG;
  436. }
  437. //
  438. // Copy the info to the caller's buffer.
  439. //
  440. m_CriticalSection.Lock();
  441. CopyMemory( pBuffer, m_RequestID.ClientUID, MCAST_CLIENT_ID_LEN );
  442. m_CriticalSection.Unlock();
  443. LOG((MSP_TRACE, "CMDhcpLeaseInfo::GetRequestIDBuffer: exit"));
  444. return S_OK;
  445. }
  446. BYTE HexDigitValue( WCHAR c )
  447. {
  448. _ASSERTE( iswxdigit(c) );
  449. c = towlower(c);
  450. if ( ( c >= L'0' ) && ( c <= L'9' ) )
  451. {
  452. return c - L'0';
  453. }
  454. else
  455. {
  456. return c - L'a' + 10;
  457. }
  458. }
  459. HRESULT CMDhcpLeaseInfo::put_RequestID(
  460. BSTR bszGuid
  461. )
  462. {
  463. LOG((MSP_TRACE, "CMDhcpLeaseInfo::put_RequestID: enter"));
  464. // (UINT) -1 is as big a value as we can give it -- we don't want any
  465. // limitation on the number of characters it checks.
  466. if ( IsBadStringPtr(bszGuid, (UINT) -1 ) )
  467. {
  468. LOG((MSP_ERROR, "CMDhcpLeaseInfo::put_RequestID: "
  469. "bad BSTR; fail"));
  470. return E_POINTER;
  471. }
  472. //
  473. // Determine the byte buffer we need to set based on the string.
  474. // The string should be MCAST_CLIENT_ID_LEN * 2 characters long;
  475. // each byte is represented by two hexadecimal digits, starting
  476. // with the most significant byte.
  477. //
  478. // Note that this format is intentionally not specified in the interface
  479. // spec; the client should not depend on the specific format. The format
  480. // we happen to use is convenient because it's printable, contains no
  481. // spaces, and is easy to generate and parse.
  482. //
  483. if ( lstrlenW( bszGuid ) < 2 * MCAST_CLIENT_ID_LEN )
  484. {
  485. LOG((MSP_ERROR, "CMDhcpLeaseInfo::put_RequestID - "
  486. "string is only %d characters long; "
  487. "we require at least %d characters - exit E_INVALIDARG",
  488. lstrlenW( bszGuid ), 2 * MCAST_CLIENT_ID_LEN));
  489. return E_INVALIDARG;
  490. }
  491. BYTE NewUID [ MCAST_CLIENT_ID_LEN ];
  492. for ( DWORD i = 0; i < MCAST_CLIENT_ID_LEN; i++ )
  493. {
  494. if ( ( ! iswxdigit( bszGuid[ 2 * i ] ) ) ||
  495. ( ! iswxdigit( bszGuid[ 2 * i + 1 ] ) ) )
  496. {
  497. LOG((MSP_ERROR, "CMDhcpLeaseInfo::put_RequestID - "
  498. "invalid value for byte %d / %d - exit E_INVALIDARG",
  499. i + 1, MCAST_CLIENT_ID_LEN ));
  500. return E_INVALIDARG;
  501. }
  502. //
  503. // Compute value of byte based on corresponding hex digits in string.
  504. //
  505. NewUID[ i ] = ( ( HexDigitValue( bszGuid[ 2 * i ] ) ) << 4 ) +
  506. HexDigitValue( bszGuid[ 2 * i + 1 ] );
  507. }
  508. //
  509. // Allocate and initialize the request id structure accordingly.
  510. // We do this only during initialization, so there is no need to
  511. // use the critical section.
  512. //
  513. // We could have just used this new'ed buffer above and avoided the
  514. // copy, but this makes the code a bit more straightforward as we
  515. // don't have to worry about cleaning up the allocation if something's
  516. // wrong with the string. No one will notice the overhead.
  517. //
  518. m_RequestID.ClientUIDLength = MCAST_CLIENT_ID_LEN;
  519. m_RequestID.ClientUID = new BYTE[ MCAST_CLIENT_ID_LEN ];
  520. if ( m_RequestID.ClientUID == NULL )
  521. {
  522. LOG((MSP_ERROR, "CMDhcpLeaseInfo::put_RequestID - "
  523. "buffer allocation failed - exit E_OUTOFMEMORY"));
  524. return E_OUTOFMEMORY;
  525. }
  526. CopyMemory(m_RequestID.ClientUID,
  527. NewUID,
  528. MCAST_CLIENT_ID_LEN
  529. );
  530. LOG((MSP_TRACE, "CMDhcpLeaseInfo::put_RequestID - exit"));
  531. return S_OK;
  532. }
  533. //////////////////////////////////////////////////////////////////////////////
  534. //////////////////////////////////////////////////////////////////////////////
  535. // IMcastLeaseInfo
  536. //
  537. // This interface can be obtained by calling IMcastAddressAllocation::CreateLeaseInfo. This
  538. // interface can also be obtained as the result of an IMcastAddressAllocation::RequestAddress
  539. // or IMcastAddressAllocation::RenewAddress call, in which case it indicates the properties of
  540. // a lease that has been granted or renewed. This is a "read-only" interface
  541. // in that it has "get" methods but no "put" methods.
  542. //////////////////////////////////////////////////////////////////////////////
  543. //////////////////////////////////////////////////////////////////////////////
  544. //////////////////////////////////////////////////////////////////////////////
  545. // IMcastLeaseInfo::get_RequestID
  546. //
  547. // Parameters
  548. // ppRequestID [out] Pointer to a BSTR (size-tagged Unicode string
  549. // pointer) that will receive the request ID for this
  550. // lease. The request ID uniquely identifies this
  551. // lease request to the server. The string is
  552. // allocated using SysAllocString(); when the caller
  553. // no longer needs the string, it should free it using
  554. // SysFreeString().
  555. //
  556. // Return Values
  557. // S_OK Success
  558. // E_POINTER The caller passed in an invalid pointer argument
  559. // E_FAIL The lease info object contains an invalid request ID
  560. // E_OUTOFMEMORY Not enough memory to allocate the BSTR
  561. //
  562. // Description
  563. // Use this method to obtain the request ID for a lease. The primary
  564. // purpose of this method is to allow you to save the request ID after
  565. // your application exits, so that you can call IMcastAddressAllocation::CreateLeaseInfo to
  566. // recreate the lease info object during a subsequent run. This allows you
  567. // to renew or release a lease after the instance of your program that
  568. // originally requested the lease has exited.
  569. //////////////////////////////////////////////////////////////////////////////
  570. STDMETHODIMP CMDhcpLeaseInfo::get_RequestID(
  571. BSTR * pbszGuid
  572. )
  573. {
  574. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_RequestID: enter"));
  575. //
  576. // Check the return pointer.
  577. //
  578. if ( IsBadWritePtr(pbszGuid, sizeof(BSTR)) )
  579. {
  580. LOG((MSP_ERROR, "CMDhcpLeaseInfo::get_RequestID: "
  581. "bad BSTR pointer; fail"));
  582. return E_POINTER;
  583. }
  584. //
  585. // Construct the string; 2 characters of string space per byte of
  586. // request ID space, plus trailing L'\0'.
  587. //
  588. WCHAR wszBuffer[ 2 * MCAST_CLIENT_ID_LEN + 1 ] = L"";
  589. WCHAR wszThisByte[ 3 ]; // string representation of one byte plus space
  590. m_CriticalSection.Lock();
  591. for ( DWORD i = 0; i < MCAST_CLIENT_ID_LEN; i++ )
  592. {
  593. swprintf( wszThisByte, L"%02x", m_RequestID.ClientUID[i] );
  594. lstrcatW( wszBuffer, wszThisByte );
  595. }
  596. m_CriticalSection.Unlock();
  597. //
  598. // Allocate a BSTR and return it.
  599. //
  600. *pbszGuid = SysAllocString(wszBuffer);
  601. if ( (*pbszGuid) == NULL )
  602. {
  603. LOG((MSP_ERROR, "CMDhcpLeaseInfo::get_RequestID: "
  604. "failed to SysAllocString - exit E_OUTOFMEMORY"));
  605. return E_OUTOFMEMORY;
  606. }
  607. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_RequestID: exit"));
  608. return S_OK;
  609. }
  610. //////////////////////////////////////////////////////////////////////////////
  611. // IMcastLeaseInfo::get_LeaseStartTime
  612. //
  613. // Parameters
  614. // pTime [out] Pointer to a DATE that will receive the start time of the
  615. // lease.
  616. //
  617. // Return Values
  618. // S_OK Success
  619. // E_POINTER The caller passed in an invalid pointer argument
  620. // E_INVALIDARG A failure occurred during date format conversion
  621. //
  622. // Description
  623. // Use this method to obtain the start time of the lease.
  624. /////////////////////////////////////////////////////////////////////////////
  625. STDMETHODIMP CMDhcpLeaseInfo::get_LeaseStartTime(
  626. DATE *pTime
  627. )
  628. {
  629. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_LeaseStartTime: enter"));
  630. m_CriticalSection.Lock();
  631. HRESULT hr = LeaseTimeToDate(m_pLease->LeaseStartTime, pTime);
  632. m_CriticalSection.Unlock();
  633. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_LeaseStartTime: exit; hr = %08x", hr));
  634. return hr;
  635. }
  636. //////////////////////////////////////////////////////////////////////////////
  637. // IMcastLeaseInfo::put_LeaseStartTime
  638. //
  639. // Parameters
  640. // time [in] A DATE specifying the start time of the lease.
  641. //
  642. // Return Values
  643. // S_OK Success
  644. // E_INVALIDARG A failure occurred during date format conversion
  645. //
  646. // Description
  647. // Use this method to set the start time of the lease. This method, along
  648. // with put_LeaseStopTime, allows you to renew a lease without calling
  649. // IMcastAddressAllocation::CreateLeaseInfo.
  650. //////////////////////////////////////////////////////////////////////////////
  651. STDMETHODIMP CMDhcpLeaseInfo::put_LeaseStartTime(
  652. DATE time
  653. )
  654. {
  655. LOG((MSP_TRACE, "CMDhcpLeaseInfo::put_LeaseStartTime: enter"));
  656. m_CriticalSection.Lock();
  657. HRESULT hr = DateToLeaseTime(time, &(m_pLease->LeaseStartTime));
  658. m_CriticalSection.Unlock();
  659. LOG((MSP_TRACE, "CMDhcpLeaseInfo::put_LeaseStartTime: exit; hr = %08x", hr));
  660. return hr;
  661. }
  662. //////////////////////////////////////////////////////////////////////////////
  663. // IMcastLeaseInfo::get_LeaseStopTime
  664. //
  665. // Parameters
  666. // pTime [out] Pointer to a DATE that will receive the stop time of the
  667. // lease.
  668. //
  669. // Return Values
  670. // S_OK Success
  671. // E_POINTER The caller passed in an invalid pointer argument
  672. // E_INVALIDARG A failure occurred during date format conversion
  673. //
  674. // Description
  675. // Use this method to obtain the stop time of the lease.
  676. /////////////////////////////////////////////////////////////////////////////
  677. STDMETHODIMP CMDhcpLeaseInfo::get_LeaseStopTime(
  678. DATE *pTime
  679. )
  680. {
  681. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_LeaseStopTime: enter"));
  682. m_CriticalSection.Lock();
  683. HRESULT hr = LeaseTimeToDate(m_pLease->LeaseEndTime, pTime);
  684. m_CriticalSection.Unlock();
  685. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_LeaseStopTime: exit; hr = %08x", hr));
  686. return hr;
  687. }
  688. /////////////////////////////////////////////////////////////////////////////
  689. // IMcastLeaseInfo::put_LeaseStopTime
  690. //
  691. // Parameters
  692. // time [in] A DATE specifying the stop time of the lease.
  693. //
  694. // Return Values
  695. // S_OK Success
  696. // E_INVALIDARG A failure occurred during date format conversion
  697. //
  698. // Description
  699. // Use this method to set the stop time of the lease. This method,
  700. // along with put_LeaseStartTime, allows you to renew a lease without
  701. // calling IMcastAddressAllocation::CreateLeaseInfo.
  702. /////////////////////////////////////////////////////////////////////////////
  703. STDMETHODIMP CMDhcpLeaseInfo::put_LeaseStopTime(
  704. DATE time
  705. )
  706. {
  707. LOG((MSP_TRACE, "CMDhcpLeaseInfo::put_LeaseStopTime: enter"));
  708. m_CriticalSection.Lock();
  709. HRESULT hr = DateToLeaseTime(time, &(m_pLease->LeaseEndTime));
  710. m_CriticalSection.Unlock();
  711. LOG((MSP_TRACE, "CMDhcpLeaseInfo::put_LeaseStopTime: exit; hr = %08x", hr));
  712. return hr;
  713. }
  714. //////////////////////////////////////////////////////////////////////////////
  715. // IMcastLeaseInfo::get_AddressCount
  716. //
  717. // Parameters
  718. // pCount [out] Pointer to a long that will receive the number of
  719. // addresses requested or granted in this lease.
  720. //
  721. // Return Values
  722. // S_OK Success
  723. // E_POINTER The caller passed in an invalid pointer argument
  724. //
  725. // Description
  726. // Use this method to obtain the number of addresses requested or granted
  727. // in this lease.
  728. /////////////////////////////////////////////////////////////////////////////
  729. STDMETHODIMP CMDhcpLeaseInfo::get_AddressCount(
  730. long *pCount
  731. )
  732. {
  733. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_AddressCount: enter"));
  734. if ( IsBadWritePtr(pCount, sizeof(long)) )
  735. {
  736. LOG((MSP_ERROR, "get_AddressCount: invalid pCount pointer "
  737. "(ptr = %08x)", pCount));
  738. return E_POINTER;
  739. }
  740. // we checked when we set it that we didn't overflow a long
  741. *pCount = (long) m_pLease->AddrCount;
  742. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_AddressCount: exit"));
  743. return S_OK;
  744. }
  745. //////////////////////////////////////////////////////////////////////////////
  746. // IMcastLeaseInfo::get_ServerAddress
  747. //
  748. // Parameters
  749. // ppAddress [out] Pointer to a BSTR (size-tagged Unicode string pointer)
  750. // that will receive a string representation of the
  751. // address of the server granting this request or
  752. // renewal, if this is the case. If lease information
  753. // object does not describe a granted lease, i.e., was
  754. // not returned by IMcastAddressAllocation::RequestAddress or
  755. // IMcastAddressAllocation::RenewAddress, then the address is reported as
  756. // the string "Unspecified".
  757. //
  758. // Return Values
  759. // S_OK Success
  760. // S_FALSE Server address unspecified
  761. // E_POINTER The caller passed in an invalid pointer argument
  762. // E_OUTOFMEMORY Not enough memory to allocate the string
  763. //
  764. // Description
  765. // Use this method to obtain a string representing the address of the
  766. // MDHCP server granting this lease.
  767. /////////////////////////////////////////////////////////////////////////////
  768. STDMETHODIMP CMDhcpLeaseInfo::get_ServerAddress(
  769. BSTR *ppAddress
  770. )
  771. {
  772. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_ServerAddress: enter"));
  773. if ( IsBadWritePtr(ppAddress, sizeof(BSTR)) )
  774. {
  775. LOG((MSP_ERROR, "get_ServerAddress: invalid ppAddress pointer "
  776. "(ptr = %08x)", ppAddress));
  777. return E_POINTER;
  778. }
  779. HRESULT hr = S_OK;
  780. WCHAR wszBuffer[100]; // no danger of overflow (see below)
  781. // SPECBUG: We should discuss what sort of behavior we want for
  782. // this case.
  783. if ( m_pLease->ServerAddress.IpAddrV4 == 0 )
  784. {
  785. wsprintf(wszBuffer, L"Unspecified");
  786. hr = S_FALSE;
  787. }
  788. else
  789. {
  790. ipAddressToStringW(wszBuffer, m_pLease->ServerAddress.IpAddrV4);
  791. }
  792. // This allocates space on OLE's heap, copies the wide character string
  793. // to that space, fille in the BSTR length field, and returns a pointer
  794. // to the wchar array part of the BSTR.
  795. *ppAddress = SysAllocString(wszBuffer);
  796. if ( *ppAddress == NULL )
  797. {
  798. LOG((MSP_ERROR, "get_ServerAddress: out of memory in string allocation"));
  799. return E_OUTOFMEMORY;
  800. }
  801. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_ServerAddress: exit: hr = %08x", hr));
  802. return hr;
  803. }
  804. //////////////////////////////////////////////////////////////////////////////
  805. // IMcastLeaseInfo::get_TTL
  806. //
  807. // Parameters
  808. // pTTL [out] Pointer to a long that will receive the TTL value associated
  809. // with this lease.
  810. //
  811. // Return Values
  812. // S_OK Success
  813. // E_POINTER The caller passed in an invalid pointer argument
  814. // E_FAIL There is no TTL associated with this lease
  815. //
  816. // Description
  817. // Use this method to obtain the TTL value associated with this lease.
  818. // This is more or less significant in the implementation of multicast
  819. // routing; generally, the higher the TTL value, the "larger" or more
  820. // inclusive the multicast scope. Probably, most applications need not
  821. // worry about the TTL.
  822. /////////////////////////////////////////////////////////////////////////////
  823. STDMETHODIMP CMDhcpLeaseInfo::get_TTL(
  824. long *pTTL
  825. )
  826. {
  827. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_TTL: enter"));
  828. if ( IsBadWritePtr(pTTL, sizeof(long)) )
  829. {
  830. LOG((MSP_ERROR, "get_TTL: invalid pTTL pointer "
  831. "(ptr = %08x)", pTTL));
  832. return E_POINTER;
  833. }
  834. if ( ! m_fGotTtl )
  835. {
  836. LOG((MSP_ERROR, "get_TTL: no TTL set"));
  837. return E_FAIL;
  838. }
  839. // we should check when we set it that we don't overflow a long
  840. // (only 0 - 255 is actually meaningful, right?)
  841. *pTTL = (long) m_lTtl;
  842. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_TTL: exit"));
  843. return S_OK;
  844. }
  845. //////////////////////////////////////////////////////////////////////////////
  846. // IMcastLeaseInfo::get_Addresses
  847. //
  848. // Parameters
  849. // pVariant [out] Pointer to a VARIANT that will receive an OLE-standard
  850. // Collection of addresses. Each address is represented
  851. // as a BSTR (size-tagged Unicode string pointer) in
  852. // "dot-quad" notation: e.g., "245.1.2.3".
  853. //
  854. // Return Values
  855. // S_OK Success
  856. // E_POINTER The caller passed in an invalid pointer argument
  857. // E_OUTOFMEMORY Not enough memory to allocate the Collection
  858. //
  859. // Description
  860. // Use this method to obtain the collection of multicast addresses that
  861. // are the subject of this lease or lease request. This method is
  862. // primarily for VB and other scripting languages; C++ programmers use
  863. // EnumerateAddresses instead.
  864. /////////////////////////////////////////////////////////////////////////////
  865. STDMETHODIMP CMDhcpLeaseInfo::get_Addresses(
  866. VARIANT * pVariant
  867. )
  868. {
  869. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_Addresses: enter"));
  870. if (IsBadWritePtr(pVariant, sizeof(VARIANT)))
  871. {
  872. LOG((MSP_ERROR, "get_Addresses: "
  873. "invalid pVariant pointer "
  874. "(ptr = %08x)", pVariant));
  875. return E_POINTER;
  876. }
  877. BSTR * pbszArray = NULL;
  878. // This performs a new and as many SysAllocStrings as there are addresses.
  879. HRESULT hr = MakeBstrArray(&pbszArray);
  880. if (FAILED(hr))
  881. {
  882. LOG((MSP_ERROR, "get_Addresses: MakeBstrArray failed"));
  883. return hr;
  884. }
  885. //
  886. // create the collection object - use the tapi collection
  887. //
  888. CComObject<CTapiBstrCollection> * p;
  889. hr = CComObject<CTapiBstrCollection>::CreateInstance( &p );
  890. if (FAILED(hr) || (p == NULL))
  891. {
  892. LOG((MSP_ERROR, "get_Addresses: Could not create CTapiBstrCollection "
  893. "object - return %lx", hr ));
  894. for (DWORD i = 0 ; i < m_pLease->AddrCount; i++)
  895. {
  896. SysFreeString(pbszArray[i]);
  897. }
  898. delete pbszArray;
  899. return hr;
  900. }
  901. // initialize it using an iterator -- pointers to the beginning and
  902. // the ending element plus one. The collection takes ownership of the
  903. // BSTRs. We no longer need the array they were kept in.
  904. hr = p->Initialize(m_pLease->AddrCount,
  905. pbszArray,
  906. pbszArray + m_pLease->AddrCount);
  907. if (FAILED(hr))
  908. {
  909. LOG((MSP_ERROR, "get_Addresses: Could not initialize "
  910. "ScopeCollection object - return %lx", hr ));
  911. for (DWORD i = 0 ; i < m_pLease->AddrCount; i++)
  912. {
  913. SysFreeString(pbszArray[i]);
  914. }
  915. delete pbszArray;
  916. delete p;
  917. return hr;
  918. }
  919. // The collection takes ownership of the BSTRs.
  920. // We no longer need the array they were kept in.
  921. delete pbszArray;
  922. // get the IDispatch interface
  923. IDispatch * pDisp;
  924. hr = p->_InternalQueryInterface( IID_IDispatch, (void **) &pDisp );
  925. if (FAILED(hr))
  926. {
  927. // Query interface failed so we don't know that if it addreffed
  928. // or not.
  929. LOG((MSP_ERROR, "get_Addresses: QI for IDispatch failed on "
  930. "ScopeCollection - %lx", hr ));
  931. delete p;
  932. return hr;
  933. }
  934. // put it in the variant
  935. LOG((MSP_INFO, "placing IDispatch value %08x in variant", pDisp));
  936. VariantInit(pVariant);
  937. pVariant->vt = VT_DISPATCH;
  938. pVariant->pdispVal = pDisp;
  939. LOG((MSP_TRACE, "CMDhcpLeaseInfo::get_Addresses: exit"));
  940. return S_OK;
  941. }
  942. //////////////////////////////////////////////////////////////////////////////
  943. // IMcastLeaseInfo::EnumerateAddresses
  944. //
  945. // Parameters
  946. // ppEnumAddresses [out] Returns a pointer to a new IEnumBstr object.
  947. // IEnumBstr is a standard enumerator interface
  948. // that enumerates BSTRs (size-tagged Unicode string
  949. // pointers). Each string is in "dot-quad" notation:
  950. // e.g., "245.1.2.3".
  951. //
  952. // Return Values
  953. // S_OK Success
  954. // E_POINTER The caller passed in an invalid pointer argument
  955. // E_OUTOFMEMORY Not enough memory to allocate the enumerator
  956. //
  957. // Description
  958. // Use this method to obtain the collection of multicast addresses that
  959. // are the subject of this lease or lease request. This method is
  960. // primarily for C++ programmers; VB and other scripting languages use
  961. // get_Addresses instead.
  962. /////////////////////////////////////////////////////////////////////////////
  963. class _CopyBSTR
  964. {
  965. public:
  966. #if _ATL_VER >= 0x0203
  967. static HRESULT copy(BSTR *p1, BSTR *p2)
  968. {
  969. (*p1) = SysAllocString(*p2);
  970. if (*p1)
  971. return S_OK;
  972. else
  973. return E_OUTOFMEMORY;
  974. }
  975. #else
  976. static void copy(BSTR *p1, BSTR *p2)
  977. {
  978. (*p1) = SysAllocString(*p2);
  979. }
  980. #endif
  981. static void init(BSTR* p) {*p = NULL;}
  982. static void destroy(BSTR* p) { SysFreeString(*p);}
  983. };
  984. STDMETHODIMP CMDhcpLeaseInfo::EnumerateAddresses(
  985. IEnumBstr ** ppEnumAddresses
  986. )
  987. {
  988. LOG((MSP_TRACE, "CMDhcpLeaseInfo::EnumerateAddresses: enter"));
  989. if (IsBadWritePtr(ppEnumAddresses, sizeof(IEnumBstr *)))
  990. {
  991. LOG((MSP_ERROR, "EnumerateAddresses: "
  992. "invalid ppEnumAddresses pointer "
  993. "(ptr = %08x)", ppEnumAddresses));
  994. return E_POINTER;
  995. }
  996. BSTR * pbszArray = NULL;
  997. // This performs a new and as many SysAllocStrings as there are addresses.
  998. HRESULT hr = MakeBstrArray(&pbszArray);
  999. if (FAILED(hr))
  1000. {
  1001. LOG((MSP_ERROR, "EnumerateAddresses: MakeBstrArray failed"));
  1002. return hr;
  1003. }
  1004. typedef CSafeComEnum<IEnumBstr, &IID_IEnumBstr,
  1005. BSTR, _CopyBSTR> CEnumerator;
  1006. CComObject<CEnumerator> *pEnum = NULL;
  1007. hr = CComObject<CEnumerator>::CreateInstance(&pEnum);
  1008. if (FAILED(hr) || (pEnum == NULL))
  1009. {
  1010. LOG((MSP_ERROR, "EnumerateAddresses: "
  1011. "Couldn't create enumerator object: %08x", hr));
  1012. for (DWORD i = 0 ; i < m_pLease->AddrCount; i++)
  1013. {
  1014. SysFreeString(pbszArray[i]);
  1015. }
  1016. delete pbszArray;
  1017. return hr;
  1018. }
  1019. // Hand the BSTRs to the enumerator. The enumerator takes ownership of the
  1020. // array of BSTRs, so no need to delete them if this succeeds.
  1021. hr = pEnum->Init(&(pbszArray[0]),
  1022. &(pbszArray[m_pLease->AddrCount]),
  1023. NULL, AtlFlagTakeOwnership);
  1024. if (FAILED(hr))
  1025. {
  1026. LOG((MSP_ERROR, "EnumerateAddresses: "
  1027. "Init enumerator object failed: %08x", hr));
  1028. for (DWORD i = 0 ; i < m_pLease->AddrCount; i++)
  1029. {
  1030. SysFreeString(pbszArray[i]);
  1031. }
  1032. delete pbszArray;
  1033. // p has not yet been addreffed so release makes no sense
  1034. delete pEnum;
  1035. return hr;
  1036. }
  1037. // The enumerator took ownership, so don't delete the array.
  1038. // Now get the interface we wanted...
  1039. hr = pEnum->_InternalQueryInterface(IID_IEnumBstr,
  1040. (void **) ppEnumAddresses);
  1041. if ( FAILED(hr) )
  1042. {
  1043. LOG((MSP_ERROR, "EnumerateAddresses: "
  1044. "internal QI failed: %08x", hr));
  1045. // we don't know if p has been addreffed
  1046. delete pEnum;
  1047. return hr;
  1048. }
  1049. LOG((MSP_TRACE, "CMDhcpLeaseInfo::EnumerateAddresses: exit"));
  1050. return S_OK;
  1051. }
  1052. /////////////////////////////////////////////////////////////////////////////
  1053. HRESULT CMDhcpLeaseInfo::GetLocal(BOOL * pfLocal)
  1054. {
  1055. LOG((MSP_TRACE, "CMDhcpLeaseInfo::GetLocal: enter"));
  1056. _ASSERTE( ! IsBadWritePtr( pfLocal, sizeof(BOOL) ) );
  1057. *pfLocal = m_fLocal;
  1058. LOG((MSP_TRACE, "CMDhcpLeaseInfo::GetLocal: exit S_OK"));
  1059. return S_OK;
  1060. }
  1061. /////////////////////////////////////////////////////////////////////////////
  1062. HRESULT CMDhcpLeaseInfo::SetLocal(BOOL fLocal)
  1063. {
  1064. LOG((MSP_TRACE, "CMDhcpLeaseInfo::SetLocal: enter"));
  1065. m_fLocal = fLocal;
  1066. LOG((MSP_TRACE, "CMDhcpLeaseInfo::SetLocal: exit S_OK"));
  1067. return S_OK;
  1068. }
  1069. // eof