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.

515 lines
16 KiB

  1. #include <winsock2.h>
  2. #include "IcsMgr.h"
  3. #include <winbase.h>
  4. #include <winreg.h>
  5. #include <tchar.h>
  6. #include <sensapi.h>
  7. #include "msobcomm.h"
  8. // #include "appdefs.h"
  9. typedef BOOL (WINAPI * LPFNDLL_ISICSAVAILABLE) ();
  10. static const DWORD ICSLAP_DIAL_STATE = 15; // As per ICS Specification
  11. static const DWORD ICSLAP_GENERAL_STATUS = 21;
  12. static CIcsMgr *ptrIcsMgr = NULL;
  13. static BOOL bIsWinsockInitialized = FALSE;
  14. static const WCHAR cszIcsHostIpAddress[] = L"192.168.0.1";
  15. extern CObCommunicationManager* gpCommMgr;
  16. // based on ICS beacon protocol
  17. typedef struct _ICS_DIAL_STATE_CB
  18. {
  19. ICS_DIAL_STATE state;
  20. DWORD options;
  21. } ICS_DIAL_STATE_CB;
  22. // used for IsIcsAvailable()
  23. const static WCHAR cszIcsKey[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\OOBE\\Ics";
  24. const static WCHAR cszIcsStatusValueName[] = L"IsIcsAvailable";
  25. // used for winsock operations
  26. static WORD wVersionRequested = MAKEWORD ( 2, 2 );
  27. static WSADATA SocketData;
  28. CIcsMgr::CIcsMgr() : m_hBotThread(0), m_dwBotThreadId(0), m_hDialThread(0), m_dwDialThreadId(0), m_pfnIcsConn(OnIcsConnectionStatus)
  29. {
  30. ptrIcsMgr = this;
  31. if ( !bIsWinsockInitialized )
  32. {
  33. if ( !WSAStartup ( wVersionRequested, &SocketData ) )
  34. {
  35. bIsWinsockInitialized = TRUE;
  36. }
  37. }
  38. return;
  39. }
  40. CIcsMgr::~CIcsMgr()
  41. {
  42. if ( m_hDialThread ) CloseHandle (m_hDialThread);
  43. if ( bIsWinsockInitialized )
  44. {
  45. //WSACleanup ();
  46. bIsWinsockInitialized = FALSE;
  47. }
  48. ptrIcsMgr = NULL;
  49. TriggerIcsCallback ( FALSE );
  50. return;
  51. }
  52. BOOL CIcsMgr::IsCallbackUsed ()
  53. {
  54. return !bReducedCallback;
  55. }
  56. // A server error during ICS is trapped by the ICS manager, instead
  57. // of the OOBE MSOBMAIN body. This gives the manager a larger sphere
  58. // of control.
  59. VOID CIcsMgr::NotifyIcsMgr(UINT msg, WPARAM wparam, LPARAM lparam)
  60. {
  61. switch (msg)
  62. {
  63. case WM_OBCOMM_ONSERVERERROR:
  64. {
  65. // on server error! is the host still available ?
  66. if ( ! IsDestinationReachable ( cszIcsHostIpAddress, NULL ) )
  67. {
  68. // fire event that Home Network is unavailable.
  69. OnIcsConnectionStatus ( ICS_HOMENET_UNAVAILABLE );
  70. }
  71. else
  72. { // this will be considered a timeout error.
  73. OnIcsConnectionStatus ( ICS_TIMEOUT );
  74. }
  75. }
  76. break;
  77. default:
  78. break;
  79. }
  80. return;
  81. }
  82. // PACKET READER ------------------
  83. // Note: Please refer to the ICS Specifications for the packet format. You can
  84. // consult [email protected] for the documentation.
  85. //
  86. // Description: This function listens for UDP packets arriving at the ICS
  87. // broadcast port. The ICS host sends notifications to the Home Network
  88. // whenever the Connection status changes at the Shared Connection. The func
  89. // reads the packet and notifies OOBE by firing a callback function (*lpParam)
  90. // which notifies OOBE via PostMessage(). A script routine can eventually be
  91. // executed to handle the notification.
  92. //
  93. // An ICS broadcast packet has the following format:
  94. // |resp:0,bcast:1,id:2-31|cbData:0-31|data(cbData - 8 bytes)|
  95. // |<---------32 bits---->|<-32 bits->|<---total_length - 8 ------>|
  96. // |<IE-1>|<IE-2>|���..��|<IE-N>|
  97. //
  98. // Each information element (IE) has the following format:
  99. // | opcode 0-31 | cbIE 0=64 |data(cbIE - 12 bytes)|
  100. // |<-- 32 bits -->|<-- 64 bits -->|<-- cbIE - 12 bytes |
  101. //
  102. DWORD WINAPI IcsDialStatusProc(LPVOID lpParam)
  103. {
  104. INT n = 0;
  105. u_short usPort = 2869;
  106. struct sockaddr_in saddr, caddr;
  107. INT caddr_len = sizeof ( caddr );
  108. BYTE rgbBuf[300];
  109. DWORD dwBufSize = sizeof ( rgbBuf );
  110. LPDWORD pdw = 0;
  111. BYTE *lpbie = 0;
  112. BYTE *lpbBound = 0;
  113. ICS_DIAL_STATE_CB *ptrDial = 0;
  114. SOCKET s = INVALID_SOCKET;
  115. PFN_ICS_CONN_CALLBACK pfn_IcsCallback = NULL;
  116. DWORD dwError = NULL;
  117. bIsDialThreadAlive = TRUE;
  118. if ( !lpParam )
  119. {
  120. bIsDialThreadAlive = FALSE;
  121. return ERROR_INVALID_PARAMETER;
  122. }
  123. if ( !bIsWinsockInitialized )
  124. {
  125. bIsDialThreadAlive = FALSE;
  126. return 0;
  127. }
  128. if ( (s = socket ( AF_INET, SOCK_DGRAM, 0 )) == INVALID_SOCKET )
  129. {
  130. bIsDialThreadAlive = FALSE;
  131. return E_FAIL; // for want of a better return value *BUGBUG*
  132. // TRACE ( L"SOCKET Error.\t:%d:\n", WSAGetLastError() );
  133. }
  134. else
  135. {
  136. __try
  137. {
  138. memset ( &saddr, 0, sizeof (saddr) );
  139. saddr.sin_family = AF_INET;
  140. saddr.sin_addr.S_un.S_addr = htonl ( INADDR_ANY );
  141. saddr.sin_port = htons ( usPort );
  142. if ( bind( s, (struct sockaddr *) &saddr, sizeof(saddr) ) == SOCKET_ERROR )
  143. {
  144. // TRACE ( L"Bind error.\n" );
  145. dwError = WSAGetLastError();
  146. }
  147. else
  148. {
  149. if (ptrIcsMgr) ptrIcsMgr->RefreshIcsDialStatus();
  150. for ( ; ; )
  151. {
  152. if ( (n = recvfrom ( s, (CHAR*)rgbBuf, dwBufSize, 0, (struct sockaddr *) &caddr, &caddr_len )) == SOCKET_ERROR )
  153. {
  154. // TRACE ( L"Socket Error.\n" );
  155. break;
  156. }
  157. lpbBound = rgbBuf+n; // this protects us from illegal packet configurations.
  158. // TRACE ( L" Something received! Size = %d\n" , n );
  159. // checking for BROADCAST packets //
  160. if ( *(pdw = (LPDWORD) rgbBuf) & 0xC0000000 )
  161. {
  162. // This is a broadcast packet! We can parse the packet.
  163. }
  164. else
  165. {
  166. // non-broadcast packets are ignored.
  167. continue;
  168. }
  169. lpbie = rgbBuf+8;
  170. while ( lpbie && ( (lpbie+8) <= lpbBound) )
  171. {
  172. if ( *(pdw = ((PDWORD)lpbie)) == ICSLAP_DIAL_STATE )
  173. {
  174. // TRACE (L"Dial State Engine. The Datasize is %d\n", pdw[2]-12);
  175. if ( (lpbie+12+sizeof(ICS_DIAL_STATE_CB)) <= lpbBound )
  176. {
  177. ptrDial = (ICS_DIAL_STATE_CB*)(lpbie+12);
  178. pfn_IcsCallback = *((PFN_ICS_CONN_CALLBACK*)lpParam);
  179. if ( pfn_IcsCallback )
  180. {
  181. pfn_IcsCallback ( ptrDial->state );
  182. }
  183. // TRACE (L"Dial State = %d\n", ptrDial->state);
  184. lpbie = 0;
  185. }
  186. else
  187. {
  188. // packet has illegal data.
  189. break;
  190. }
  191. }
  192. else
  193. {
  194. // not the correct ie.
  195. if ( (lpbie += pdw[2]) >= lpbBound )
  196. {
  197. // we traversed the packet without finding the correct ie.
  198. // TRACE (L"Done.\n");
  199. lpbie = 0;
  200. }
  201. // else we continue the loop.
  202. }
  203. }
  204. }
  205. }
  206. }
  207. __finally
  208. {
  209. // graceful shutdown of the socket.
  210. shutdown ( s, SD_BOTH );
  211. closesocket ( s );
  212. }
  213. }
  214. bIsDialThreadAlive = FALSE;
  215. return ERROR_SUCCESS;
  216. }
  217. // this is the callback routine that reports ICS connection state information.
  218. // it relies on both the Beacon protocol and Internet Explorer's error handling
  219. // (see ONSERVERERROR for details.)
  220. VOID CALLBACK OnIcsConnectionStatus(ICS_DIAL_STATE dwIcsConnectionStatus)
  221. {
  222. eIcsDialState = dwIcsConnectionStatus;
  223. if ( !gpCommMgr ) return;
  224. TRACE1(L"ICS Connection Status %d", dwIcsConnectionStatus);
  225. // we are not interested in the modem scenario. only ics-broadband is supported.
  226. if ( (dwIcsConnectionStatus == ICSLAP_CONNECTING) ||
  227. (dwIcsConnectionStatus == ICSLAP_CONNECTED) ||
  228. (dwIcsConnectionStatus == ICSLAP_DISCONNECTING) ||
  229. (dwIcsConnectionStatus == ICSLAP_DISCONNECTED) )
  230. {
  231. bIsBroadbandIcsAvailable = FALSE;
  232. return;
  233. }
  234. // indication of ics-broadband
  235. if (dwIcsConnectionStatus == ICSLAP_PERMANENT)
  236. bIsBroadbandIcsAvailable = TRUE;
  237. // none of the other states will change the bIsBroadbandIcsAvailable value.
  238. // if the callback mechanism has been turned off, we will not report
  239. // connection status to the upper application layer(s).
  240. if ( bReducedCallback )
  241. {
  242. return;
  243. }
  244. PostMessage ( gpCommMgr->m_hwndCallBack, WM_OBCOMM_ONICSCONN_STATUS, (WPARAM)0, (LPARAM)dwIcsConnectionStatus);
  245. }
  246. // by turning this ON or OFF ( TRUE / FALSE respectively ), we can control
  247. // whether or not to inform OOBE of ICS-connection status changes.
  248. VOID CIcsMgr::TriggerIcsCallback(BOOL bStatus)
  249. {
  250. bReducedCallback = !bStatus; // if we want to un-trigger the callback, we go to "sleep" state.
  251. if ( bStatus )
  252. {
  253. RefreshIcsDialStatus();
  254. }
  255. }
  256. // Obsolete, but retained in case the beacon protocol becomes
  257. // functional. This function used to call an ICS API to check if ICS was available.
  258. // this is no longer useful for 2 reasons:
  259. // 1. We ONLY want one type of ICS (broadband, as opposed to Dial-up)
  260. // 2. The function does not report ICS availability if the machine it is called in
  261. // is the ICS HOST itself.
  262. DWORD IcsEngine(LPVOID lpParam) {
  263. // lpParam is ignored.
  264. HINSTANCE hIcsDll = NULL;
  265. LPFNDLL_ISICSAVAILABLE lpfndll_IsIcsAvailable = NULL;
  266. BOOL bIsIcsAvailable = FALSE;
  267. HKEY hIcsRegKey = 0;
  268. LONG lRetVal = 0;
  269. ICSSTATUS dwIcsStatus = ICS_ENGINE_NOT_COMPLETE;
  270. DWORD nRet = 0;
  271. DWORD dwStatus = 0;
  272. nRet = RegCreateKeyEx ( HKEY_LOCAL_MACHINE,
  273. cszIcsKey,
  274. 0,
  275. L"",
  276. REG_OPTION_NON_VOLATILE,
  277. KEY_WRITE,
  278. NULL,
  279. &hIcsRegKey,
  280. &dwStatus
  281. );
  282. if (nRet != ERROR_SUCCESS)
  283. {
  284. // Registry APIs refuse to create key. No point continuing farther.
  285. return (nRet = GetLastError());
  286. }
  287. __try {
  288. if ( !(hIcsDll = LoadLibrary(L"ICSAPI32.DLL")) )
  289. {
  290. nRet = GetLastError();
  291. dwIcsStatus = ICS_ENGINE_FAILED;
  292. __leave;
  293. }
  294. if ( !(lpfndll_IsIcsAvailable = (LPFNDLL_ISICSAVAILABLE) GetProcAddress (hIcsDll, "IsIcsAvailable")))
  295. {
  296. // We record in the registry that the engine was not initializable.
  297. nRet = GetLastError();
  298. dwIcsStatus = ICS_ENGINE_FAILED;
  299. FreeLibrary ( hIcsDll );
  300. __leave;
  301. }
  302. dwIcsStatus = ICS_ENGINE_NOT_COMPLETE;
  303. if ((nRet = RegSetValueEx(hIcsRegKey, cszIcsStatusValueName, 0, REG_DWORD, (BYTE*)&dwIcsStatus, sizeof(DWORD))) != ERROR_SUCCESS)
  304. {
  305. nRet = GetLastError();
  306. dwIcsStatus = ICS_ENGINE_FAILED;
  307. __leave;
  308. }
  309. else
  310. {
  311. __try
  312. {
  313. if (bIsIcsAvailable = lpfndll_IsIcsAvailable()) {
  314. // ICS is available
  315. dwIcsStatus = ICS_IS_AVAILABLE;
  316. nRet = ERROR_SUCCESS;
  317. } else {
  318. dwIcsStatus = ICS_IS_NOT_AVAILABLE;
  319. nRet = ERROR_SUCCESS;
  320. }
  321. }
  322. // exception-handlign is used to prevent IsIcsAvailable from
  323. // killing OOBE by generating an Invalid Page Fault.
  324. __except (EXCEPTION_EXECUTE_HANDLER)
  325. {
  326. dwIcsStatus = ICS_IS_NOT_AVAILABLE;
  327. nRet = ERROR_SUCCESS;
  328. }
  329. }
  330. }
  331. __finally
  332. {
  333. // perform registry update of the status.
  334. if ((nRet = RegSetValueEx (hIcsRegKey, cszIcsStatusValueName, 0, REG_DWORD, (BYTE*)&dwIcsStatus, sizeof(DWORD))) != ERROR_SUCCESS)
  335. {
  336. nRet = GetLastError();
  337. }
  338. RegCloseKey (hIcsRegKey);
  339. // unload library
  340. if (hIcsDll) FreeLibrary (hIcsDll);
  341. }
  342. return nRet;
  343. }
  344. // not used. see remarks for IcsEngine() above.
  345. DWORD CIcsMgr::CreateIcsBot()
  346. {
  347. LPTHREAD_START_ROUTINE lpfn_ThreadProc = (LPTHREAD_START_ROUTINE) IcsEngine;
  348. m_hBotThread = CreateThread (NULL, NULL, lpfn_ThreadProc, 0, 0, &m_dwBotThreadId);
  349. if (!m_hBotThread)
  350. {
  351. // Thread was not created
  352. m_dwBotThreadId = 0;
  353. m_hBotThread = 0;
  354. return ICSMGR_ICSBOT_CREATION_FAILED;
  355. } else
  356. {
  357. return ICSMGR_ICSBOT_CREATED;
  358. }
  359. }
  360. // this function spawns a thread that listens for ICS connectivity changes on the Host machine.
  361. // the function will ALSO work on the Host machine itself.
  362. // this uses UDP sockets. See the Ics beacon protocol [bjohnson] for details.
  363. DWORD CIcsMgr::CreateIcsDialMgr()
  364. {
  365. LPTHREAD_START_ROUTINE lpfn_ThreadProc = (LPTHREAD_START_ROUTINE) IcsDialStatusProc;
  366. if ( bIsDialThreadAlive || m_hDialThread || m_dwDialThreadId)
  367. {
  368. return ERROR_SERVICE_ALREADY_RUNNING;
  369. }
  370. m_hDialThread = CreateThread (NULL, NULL, lpfn_ThreadProc, (LPVOID)(&m_pfnIcsConn), 0, &m_dwDialThreadId);
  371. if (!m_hDialThread)
  372. {
  373. // Thread was not created
  374. m_hDialThread = 0;
  375. m_dwDialThreadId = 0;
  376. return GetLastError();
  377. }
  378. else
  379. {
  380. return ERROR_SUCCESS;
  381. }
  382. }
  383. // this now relies
  384. BOOL CIcsMgr::IsIcsAvailable() {
  385. return bIsBroadbandIcsAvailable;
  386. }
  387. BOOL CIcsMgr::IsIcsHostReachable()
  388. {
  389. return IsDestinationReachable ( cszIcsHostIpAddress, 0 );
  390. }
  391. DWORD CIcsMgr::RefreshIcsDialStatus()
  392. {
  393. INT n = 0;
  394. u_short usServerPort = 2869;
  395. struct sockaddr_in saddr;
  396. INT saddr_len = sizeof ( saddr );
  397. BYTE lpbRequestBuf[100];
  398. DWORD dwRequestBufSize = sizeof ( lpbRequestBuf );
  399. LPDWORD pdw = 0;
  400. WCHAR *lpbie = 0;
  401. WCHAR *lpbBound = 0;
  402. SOCKET s = INVALID_SOCKET;
  403. DWORD nRet = ERROR_SUCCESS;
  404. if ( !bIsWinsockInitialized )
  405. {
  406. return WSANOTINITIALISED;
  407. }
  408. if ( (s = socket ( AF_INET, SOCK_DGRAM, 0 )) == INVALID_SOCKET )
  409. {
  410. return E_FAIL; // for want of a better return value *BUGBUG*
  411. // TRACE ( L"SOCKET Error.\t:%d:\n", WSAGetLastError() );
  412. }
  413. else
  414. {
  415. __try
  416. {
  417. USES_CONVERSION;
  418. memset ( &saddr, 0, sizeof (saddr) );
  419. saddr.sin_family = AF_INET;
  420. saddr.sin_addr.S_un.S_addr = inet_addr (W2A(cszIcsHostIpAddress));
  421. saddr.sin_port = htons ( usServerPort );
  422. // set up request packet:
  423. memset ( lpbRequestBuf, 0, sizeof( lpbRequestBuf ) );
  424. // setting up the request buffer.
  425. pdw = (PDWORD) lpbRequestBuf;
  426. pdw[0] = 125152 & ~(0xC0000000); // random ID
  427. pdw[1] = 20;
  428. pdw[2] = ICSLAP_GENERAL_STATUS & ~(0x80000000);
  429. pdw[3] = 0;
  430. pdw[4] = 12;
  431. if ( (n = sendto ( s, (CHAR*)lpbRequestBuf, 20, 0, (struct sockaddr *) &saddr, saddr_len )) == SOCKET_ERROR )
  432. {
  433. nRet = WSAGetLastError();
  434. __leave;
  435. }
  436. else
  437. {
  438. nRet = ERROR_SUCCESS;
  439. __leave;
  440. }
  441. }
  442. __finally
  443. {
  444. // graceful shutdown of the socket.
  445. shutdown ( s, SD_BOTH );
  446. closesocket ( s );
  447. }
  448. }
  449. return nRet;
  450. }