Source code of Windows XP (NT5)
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.

699 lines
21 KiB

  1. #include "stdafx.h"
  2. #include "natobjs.h"
  3. #include "resource.h"
  4. #include "ConnectDlg.h"
  5. #include <wincrypt.h>
  6. HRESULT CreateMD5Hash( IN PBYTE pbData,
  7. IN DWORD cbData,
  8. OUT PBYTE pbHashBuffer,
  9. IN OUT DWORD *pdwHashBufferSize );
  10. //-------------------------------------------------------------------------
  11. CNATServerComputer::CNATServerComputer( LPCTSTR pszComputerName )
  12. {
  13. m_csNatComputer = pszComputerName;
  14. ZeroMemory( m_hash, sizeof(m_hash) );
  15. }
  16. //-------------------------------------------------------------------------
  17. CNATServerComputer::~CNATServerComputer()
  18. {
  19. // clean up the lists
  20. EmptySiteComputers();
  21. EmptyGroups();
  22. }
  23. //-------------------------------------------------------------------------
  24. // Edit the properties of this server computer
  25. BOOL CNATServerComputer::OnProperties()
  26. {
  27. AfxMessageBox(IDS_NODENAME);
  28. return FALSE;
  29. }
  30. //-------------------------------------------------------------------------
  31. // get the number of groups associated with this machine
  32. DWORD CNATServerComputer::GetNumGroups()
  33. {
  34. return m_rgbGroups.GetSize();
  35. }
  36. //-------------------------------------------------------------------------
  37. // Get a group reference
  38. CNATGroup* CNATServerComputer::GetGroup( IN DWORD iGroup )
  39. {
  40. return m_rgbGroups[iGroup];
  41. }
  42. //-------------------------------------------------------------------------
  43. // Technically, the GetGroupName function is unecessary because
  44. // you get get the group class pointer, then call GetName on it, but it is really
  45. // handy to just do it here. Cleans up the snapin part of the code.
  46. BOOL CNATServerComputer::GetGroupName( IN DWORD iGroup, OUT CString &csName )
  47. {
  48. // just get the group and turn around the name
  49. CNATGroup* pGroup = GetGroup( iGroup );
  50. if ( !pGroup ) return FALSE;
  51. // get the name
  52. csName = pGroup->GetName();
  53. return TRUE;
  54. }
  55. //-------------------------------------------------------------------------
  56. // call this to automatically verify that the target NAT computers config info
  57. // hasn't changed. If it has, it prompts the user and lets them continue or refresh.
  58. BOOL CNATServerComputer::VerifyHashIsOK()
  59. {
  60. BYTE hash[HASH_BYTE_SIZE];
  61. DWORD cbHash = sizeof(hash);
  62. // get the has of the current state of the server. Passing in a null pointer
  63. // causes the routine to go out and get a new copy of the blob
  64. if ( !GetNATStateHash( NULL, 0, hash, &cbHash ) )
  65. return FALSE;
  66. // if the two hashes are the same, then the blob has not changed on us
  67. return ( memcmp(hash, m_hash, HASH_BYTE_SIZE) == 0 );
  68. }
  69. //-------------------------------------------------------------------------
  70. // rebuild all the data based on a new blob from the NAT machine
  71. void CNATServerComputer::Refresh()
  72. {
  73. DWORD num, i;
  74. CString szName, szIP;
  75. LPWSTR pszwName, pszwIP;
  76. BOOL f;
  77. // empty any current entries in the lists
  78. EmptySiteComputers();
  79. EmptyGroups();
  80. // Get the blob data from the DCOM object and put it in the wrapper thing
  81. CIPMap IpMap;
  82. // get the data
  83. LPBYTE pData = NULL;
  84. DWORD cbData = 0;
  85. // throws up its own errors
  86. pData = PGetNATStateBlob( &cbData );
  87. if ( !pData )
  88. return;
  89. // unserialize the data into the wrapper class
  90. if ( !IpMap.Unserialize(&pData, &cbData) )
  91. {
  92. goto error;
  93. }
  94. // start by reading in all the site computers from the wrapper
  95. num = IpMap.ComputerCount();
  96. for ( i = 0; i < num; i++ )
  97. {
  98. // the computer name from the wrapper
  99. if ( !IpMap.EnumComputer(i, &pszwName) )
  100. {
  101. ASSERT( FALSE );
  102. continue;
  103. }
  104. szName = pszwName;
  105. // check if the computer name is visible
  106. BOOL bVisible = CanSeeComputer( (LPCTSTR)szName );
  107. // create the computer object
  108. CNATSiteComputer* pSC = new CNATSiteComputer( (LPCTSTR)szName, bVisible );
  109. ASSERT( pSC );
  110. if ( !pSC )
  111. goto error;
  112. // add the computer to the site computer array
  113. m_rgbSiteComputers.Add( pSC );
  114. }
  115. // now read the groups. In each group, build its site array
  116. num = IpMap.IpPublicCount();
  117. for ( i = 0; i < num; i++ )
  118. {
  119. DWORD dwSticky;
  120. // the computer name from the wrapper
  121. if ( !IpMap.EnumIpPublic(i, &pszwIP, &pszwName, &dwSticky) )
  122. {
  123. ASSERT( FALSE );
  124. continue;
  125. }
  126. szIP = pszwIP;
  127. szName = pszwName;
  128. // create the group object
  129. CNATGroup* pG = new CNATGroup( this, (LPCTSTR)szIP, (LPCTSTR)szName, dwSticky );
  130. ASSERT( pG );
  131. if ( !pG )
  132. goto error;
  133. // add the computer to the site computer array
  134. m_rgbGroups.Add( pG );
  135. // now, for this group, add the sites that are associated with it. This means
  136. // looping through all the computer objects and looking for ones that match this one
  137. DWORD numComputers = m_rgbSiteComputers.GetSize();
  138. DWORD iComp;
  139. for ( iComp = 0; iComp < numComputers; iComp++ )
  140. {
  141. szIP.Empty();
  142. // if there is a match, add it as a site
  143. if ( IpMap.GetIpPrivate(iComp, i, &pszwIP, &pszwName) )
  144. {
  145. szIP = pszwIP;
  146. szName = pszwName;
  147. }
  148. // if there is a match, then there will be data in szIP
  149. if ( !szIP.IsEmpty() )
  150. {
  151. // we got a match. We can now create a new site object and add it to the group
  152. pG->AddSite( m_rgbSiteComputers[iComp], (LPCTSTR)szIP, (LPCTSTR)szName );
  153. }
  154. }
  155. }
  156. // success!
  157. goto cleanup;
  158. // failure :-(
  159. error:
  160. // tell the user about it
  161. AfxMessageBox( IDS_BADBLOB );
  162. // empty any current entries in the lists
  163. EmptySiteComputers();
  164. EmptyGroups();
  165. // cleanup
  166. cleanup:
  167. if ( pData )
  168. GlobalFree( pData );
  169. }
  170. //-------------------------------------------------------------------------
  171. // commit the current state of the data back to the NAT machine
  172. void CNATServerComputer::Commit()
  173. {
  174. #define ICOMP_ERR 0xFFFFFFFF
  175. CStoreXBF xbfStorage;
  176. // start the commit process by verifying that no other admin has changed the
  177. // state of the server since we last refreshed. If then have, we have to tell
  178. // the user and let them choose to continue commiting (losing the current state
  179. // of the server) or to cancel the current operation and automatically refresh
  180. // to the new state of the server.
  181. if ( !VerifyHashIsOK() )
  182. {
  183. if ( AfxMessageBox( IDS_VERIFYFAILED ) == IDNO )
  184. {
  185. Refresh();
  186. return;
  187. }
  188. }
  189. // we are committing. The process involves creating a new interface wrapper.
  190. // building up all the values, then writing out the blob to the DCOM layer.
  191. CIPMap IpMap;
  192. // first, add all the SiteComputer objects to the map object. While doing this, only
  193. // actually add ones that have a refcount associated with them. (cleans up items that)
  194. // are no longer used. Also, set the actual index on the object for use later on
  195. DWORD num = m_rgbSiteComputers.GetSize();
  196. DWORD i, j;
  197. DWORD iComp = 0;
  198. // loop through the computers and add each one.
  199. for ( i = 0; i < num; i++ )
  200. {
  201. CNATSiteComputer* pSC = m_rgbSiteComputers[i];
  202. ASSERT( pSC );
  203. // if the SC has a refcount, add it to the interface builder
  204. if ( pSC->m_refcount )
  205. {
  206. // set its internal iComp member variable
  207. pSC->m_iComp = iComp;
  208. // Add the SC to the interface builder
  209. if ( !IpMap.AddComputer((LPTSTR)(LPCTSTR)pSC->m_csName) )
  210. goto error;
  211. }
  212. else
  213. // the refcount is null
  214. {
  215. // set its internal iComp member variable to invalid
  216. pSC->m_iComp = ICOMP_ERR;
  217. }
  218. }
  219. // loop through the groups and add those to the interface builder
  220. num = m_rgbGroups.GetSize();
  221. // loop through the computers and add each one.
  222. for ( i = 0; i < num; i++ )
  223. {
  224. CNATGroup* pG = m_rgbGroups[i];
  225. ASSERT( pG );
  226. // add it to the interface builder
  227. if ( !IpMap.AddIpPublic((LPTSTR)pG->GetIP(), (LPTSTR)pG->GetName(), pG->GetSticky()) )
  228. goto error;
  229. // we also need to add the sites to the group
  230. DWORD numSites = pG->GetNumSites();
  231. for ( j = 0; j < numSites; j++ )
  232. {
  233. CNATSite* pS = pG->GetSite( j );
  234. ASSERT( pS );
  235. ASSERT( pS->m_pSiteComputer );
  236. ASSERT( pS->m_pSiteComputer->m_refcount > 0 );
  237. ASSERT( pS->m_pSiteComputer->m_iComp != ICOMP_ERR );
  238. // map the site to the group in the interface builder
  239. if ( !IpMap.SetIpPrivate( pS->m_pSiteComputer->m_iComp, i,
  240. (LPTSTR)pS->GetIP(), (LPTSTR)pS->GetName() ) )
  241. goto error;
  242. }
  243. }
  244. // serialize it all into a buffer for the COM call
  245. if ( !IpMap.Serialize(&xbfStorage) )
  246. goto error;
  247. // Set the blob into the DCOM layer. - It puts up its own error
  248. // message if there is a failure talking to the DCOM object
  249. if ( SetStateBlob( xbfStorage.GetBuff(), xbfStorage.GetUsed() ) )
  250. {
  251. DWORD cbHash = HASH_BYTE_SIZE;
  252. // update the hash
  253. GetNATStateHash( xbfStorage.GetBuff(), xbfStorage.GetUsed(), m_hash, &cbHash );
  254. }
  255. // return normally (success)
  256. return;
  257. error:
  258. // shouldn't ever get here
  259. ASSERT( FALSE );
  260. AfxMessageBox( IDS_BUILDBLOBERROR );
  261. }
  262. //-------------------------------------------------------------------------
  263. // empties and frees all the site computer objects in the list
  264. void CNATServerComputer::EmptySiteComputers()
  265. {
  266. DWORD numSiteComputers = m_rgbSiteComputers.GetSize();
  267. // loop the array and free all the group objects
  268. for ( DWORD i = 0; i < numSiteComputers; i++ )
  269. {
  270. delete m_rgbSiteComputers[i];
  271. }
  272. // empty the array itself
  273. m_rgbSiteComputers.RemoveAll();
  274. }
  275. //-------------------------------------------------------------------------
  276. // empties and frees all the groups/sites in the groups list
  277. void CNATServerComputer::EmptyGroups()
  278. {
  279. DWORD numGroups = m_rgbGroups.GetSize();
  280. // loop the array and free all the group objects
  281. for ( DWORD i = 0; i < numGroups; i++ )
  282. {
  283. delete m_rgbGroups[i];
  284. }
  285. // empty the array itself
  286. m_rgbGroups.RemoveAll();
  287. }
  288. //-------------------------------------------------------------------------
  289. // add a new group to the server computer (called by the UI). This
  290. // may or may not prompt the user with UI and returns the pointer
  291. // to the new group after adding it to the group list
  292. CNATGroup* CNATServerComputer::NewGroup()
  293. {
  294. // for now, make a group with just all the defaults
  295. CNATGroup* pG = new CNATGroup( this );
  296. if ( pG == NULL )
  297. {
  298. AfxMessageBox( IDS_LOWMEM );
  299. return NULL;
  300. }
  301. // Ask the user to edit the group's properties. Do not add it if they cancel
  302. if ( !pG->OnProperties() )
  303. {
  304. delete pG;
  305. return NULL;
  306. }
  307. // add the object to the end of the array
  308. m_rgbGroups.Add( pG );
  309. return pG;
  310. }
  311. //-------------------------------------------------------------------------
  312. // add a new computer to the sites list. - Note: if the computer
  313. // already exists in the sites list, it will just return a reference
  314. // to the existing computer. This routine prompts the user to choose
  315. // a computer to add. Returns FALSE if it fails.
  316. CNATSiteComputer* CNATServerComputer::NewComputer()
  317. {
  318. // Ask the user for the machine to connect to.
  319. CConnectDlg dlgConnect;
  320. if ( dlgConnect.DoModal() == IDCANCEL )
  321. return NULL;
  322. // check if it is visible
  323. BOOL bVisible = CanSeeComputer( (LPCTSTR)dlgConnect.m_cstring_name );
  324. // for now, make a group with just all the defaults
  325. CNATSiteComputer* pSC = new CNATSiteComputer( (LPCTSTR)dlgConnect.m_cstring_name, bVisible );
  326. if ( pSC == NULL )
  327. {
  328. AfxMessageBox( IDS_LOWMEM );
  329. return NULL;
  330. }
  331. // add the object to the end of the array
  332. m_rgbSiteComputers.Add( pSC );
  333. return pSC;
  334. }
  335. //-------------------------------------------------------------------------
  336. // adds an existing computer to the list - to be called during a refresh.
  337. // this checks the visiblity of the machine on the net as it adds it
  338. void CNATServerComputer::AddSiteComputer( LPWSTR pszwName )
  339. {
  340. // check if the named computer is visible on the network
  341. BOOL bVisible = CanSeeComputer( pszwName );
  342. // create the new Site Computer object
  343. CNATSiteComputer* pSC = new CNATSiteComputer( pszwName, bVisible );
  344. if ( pSC == NULL )
  345. {
  346. AfxMessageBox( IDS_LOWMEM );
  347. return;
  348. }
  349. // add the object to the end of the array
  350. m_rgbSiteComputers.Add( pSC );
  351. }
  352. //-------------------------------------------------------------------------
  353. // adds an existing group to the list - to be called during a refresh.
  354. void CNATServerComputer::AddGroup( LPWSTR pszwIPPublic, LPWSTR pszwName, DWORD dwSticky, DWORD type )
  355. {
  356. // create the new Site Computer object
  357. CNATGroup* pG = new CNATGroup( this, pszwIPPublic, pszwName, dwSticky, type );
  358. if ( pG == NULL )
  359. {
  360. AfxMessageBox( IDS_LOWMEM );
  361. return;
  362. }
  363. // add the object to the end of the array
  364. m_rgbGroups.Add( pG );
  365. }
  366. //-------------------------------------------------------------------------
  367. // gets the hash of the NAT data blob. If a null pointer to the blob
  368. // is passed in, then it dynamically gets the blob from the server
  369. // or you can pass in a specific blob. This is to be used to check if
  370. // the state of the server has changed since the data was last loaded.
  371. // The hash is obtained from the crypto code so it is really good. The
  372. // buffer for the hash should be 128 bits in length. Using an MD5 hash.
  373. BOOL CNATServerComputer::GetNATStateHash( IN LPBYTE pData, IN DWORD cbData,
  374. OUT LPBYTE pHash, IN OUT DWORD* pcbHash )
  375. {
  376. HRESULT hRes;
  377. LPBYTE pInternalData = NULL;
  378. // if no data buffer was specified, then we should get a new state blob from the server
  379. if ( !pData )
  380. {
  381. pInternalData = PGetNATStateBlob( &cbData );
  382. if ( !pInternalData )
  383. return FALSE;
  384. pData = pInternalData;
  385. }
  386. // get the hash
  387. hRes = CreateMD5Hash( pData, cbData, pHash, pcbHash );
  388. // if we allocated an internal buffer, free it now
  389. if ( pInternalData )
  390. GlobalFree( pInternalData );
  391. return SUCCEEDED( hRes );
  392. }
  393. //-------------------------------------------------------------------------
  394. // access the server and retrieve the state blob. Use GetLastError to see
  395. // what went wrong if the returned result is NULL. Pass in the dword pointed
  396. // to by pcbData to get the required size.
  397. LPBYTE CNATServerComputer::PGetNATStateBlob( OUT DWORD* pcbData )
  398. {
  399. HRESULT hRes;
  400. IMSIisLb* pIisLb;
  401. LPBYTE pData = NULL;
  402. // get the interface to the NAT server
  403. hRes = GetNATInterface( &pIisLb );
  404. if ( FAILED(hRes) )
  405. return pData;
  406. // the first call gets the amout of required space for the blob
  407. DWORD dwcb;
  408. hRes = pIisLb->GetIpList( 0, NULL, &dwcb );
  409. // if it fails, with anything except too small a buffer, then fail
  410. if ( FAILED(hRes) && (hRes != ERROR_INSUFFICIENT_BUFFER) )
  411. goto cleanup;
  412. // allocate space to receive the blob
  413. pData = (LPBYTE)GlobalAlloc( GPTR, dwcb );
  414. if ( !pData )
  415. {
  416. AfxMessageBox( IDS_LOWMEM );
  417. goto cleanup;
  418. }
  419. // get the blob
  420. hRes = pIisLb->GetIpList( dwcb, pData, &dwcb );
  421. // if it fails, with anything except too small a buffer, then fail
  422. if ( FAILED(hRes) )
  423. {
  424. GlobalFree( pData );
  425. pData = NULL;
  426. }
  427. // cleanup
  428. cleanup:
  429. pIisLb->Release();
  430. return NULL;
  431. }
  432. //-------------------------------------------------------------------------
  433. // access the server and set the state blob. Use GetLastError to see what went
  434. // wrong if the returned result is FALSE
  435. BOOL CNATServerComputer::SetStateBlob( IN LPBYTE pData, IN DWORD cbData )
  436. {
  437. HRESULT hRes;
  438. IMSIisLb* pIisLb;
  439. // get the interface to the NAT server
  440. hRes = GetNATInterface( &pIisLb );
  441. if ( FAILED(hRes) )
  442. return FALSE;
  443. // set the data blob to the server.
  444. hRes = pIisLb->SetIpList( cbData, (PUCHAR)pData );
  445. // cleanup
  446. pIisLb->Release();
  447. return FALSE;
  448. }
  449. //-------------------------------------------------------------------------
  450. // open the DCOM interface to the target NAT machine.
  451. HRESULT CNATServerComputer::GetNATInterface( IMSIisLb** ppIisLb )
  452. {
  453. COSERVERINFO csiMachineName;
  454. LPSTR pszMachineName = NULL;
  455. IClassFactory* pcsfFactory = NULL;
  456. HRESULT hRes = 0;
  457. //fill the structure for CoCreateInstanceEx
  458. ZeroMemory( &csiMachineName, sizeof(csiMachineName) );
  459. csiMachineName.pwszName = NULL;
  460. // get the class factory
  461. hRes = CoGetClassObject(CLSID_MSIisLb, CLSCTX_SERVER, &csiMachineName,
  462. IID_IClassFactory, (void**) &pcsfFactory);
  463. if ( SUCCEEDED( hRes ) )
  464. {
  465. // the instance of the load balancing interface
  466. hRes = pcsfFactory->CreateInstance(NULL, IID_IMSIisLb, (void **)ppIisLb);
  467. // clean up the class factory
  468. pcsfFactory->Release();
  469. }
  470. // if there was an error, tell the user
  471. if ( FAILED( hRes ) )
  472. {
  473. AfxMessageBox( IDS_NOLBCONNECT );
  474. }
  475. return hRes;
  476. }
  477. //-------------------------------------------------------------------------
  478. // utility to check if the computer is visible on the net
  479. BOOL CNATServerComputer::CanSeeComputer( LPCTSTR pszname )
  480. {
  481. // if no name is passed in, then it is the local machine. return true
  482. if ( (pszname == NULL) || (*pszname == 0 ) )
  483. return TRUE;
  484. // until I can figure out a way to ping the address via TCP/IP,
  485. // attempt to connect to registry.
  486. HKEY hkResult;
  487. LONG err;
  488. err = RegConnectRegistry(
  489. pszname, // address of name of remote computer
  490. HKEY_LOCAL_MACHINE, // predefined registry handle
  491. &hkResult // address of buffer for remote registry handle
  492. );
  493. // clean up
  494. if ( err == ERROR_SUCCESS )
  495. CloseHandle( hkResult );
  496. // return whether or not it worked
  497. return ( err == ERROR_SUCCESS );
  498. }
  499. //-------------------------------------------------------------------------
  500. // function is courtesy Alex Mallet (amallet)
  501. HRESULT CreateMD5Hash( IN PBYTE pbData,
  502. IN DWORD cbData,
  503. OUT PBYTE pbHashBuffer,
  504. IN OUT DWORD *pdwHashBufferSize )
  505. /*++
  506. Routine Description:
  507. Creates MD5 hash of data
  508. Arguments:
  509. pbData - buffer of data to be hashed
  510. cbData - size of data to be hashed
  511. pbHashBuffer - buffer to receive hash
  512. pdwHashBufferSize - size of pbHashBuffer
  513. Returns:
  514. HRESULT indicating success/failure
  515. --*/
  516. {
  517. HCRYPTPROV hProv = NULL;
  518. HCRYPTHASH hHash = NULL;
  519. HRESULT hRes = S_OK;
  520. //
  521. // Get a handle to the CSP that will create the
  522. // hash
  523. if ( !CryptAcquireContext( &hProv,
  524. NULL,
  525. NULL,
  526. PROV_RSA_FULL,
  527. CRYPT_VERIFYCONTEXT ) )
  528. {
  529. hRes = RETURNCODETOHRESULT( GetLastError() );
  530. goto EndCreateHash;
  531. }
  532. //
  533. // Get a handle to an MD5 hash object
  534. //
  535. if ( !CryptCreateHash( hProv,
  536. CALG_MD5,
  537. 0,
  538. 0,
  539. &hHash ) )
  540. {
  541. hRes = RETURNCODETOHRESULT( GetLastError() );
  542. goto EndCreateHash;
  543. }
  544. //
  545. // Hash the data
  546. //
  547. if ( !CryptHashData( hHash,
  548. pbData,
  549. cbData,
  550. 0 ) )
  551. {
  552. hRes = RETURNCODETOHRESULT( GetLastError() );
  553. goto EndCreateHash;
  554. }
  555. //
  556. // Retrieve the hash
  557. //
  558. if ( !CryptGetHashParam( hHash,
  559. HP_HASHVAL,
  560. pbHashBuffer,
  561. pdwHashBufferSize,
  562. 0 ) )
  563. {
  564. hRes = RETURNCODETOHRESULT( GetLastError() );
  565. goto EndCreateHash;
  566. }
  567. EndCreateHash:
  568. //
  569. //Cleanup
  570. //
  571. if ( hHash )
  572. {
  573. CryptDestroyHash( hHash );
  574. }
  575. if ( hProv )
  576. {
  577. CryptReleaseContext( hProv,
  578. 0 );
  579. }
  580. return hRes;
  581. }