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.

461 lines
14 KiB

  1. /*++
  2. Copyright (c) 2002 Microsoft Corporation
  3. Module Name:
  4. apsvcc.cpp
  5. Abstract:
  6. Implements the client side L-RPC functions for the Auto-Proxy Service.
  7. Author:
  8. Biao Wang (biaow) 10-May-2002
  9. --*/
  10. #include "wininetp.h"
  11. #include "apsvc.h"
  12. #include "..\apsvc\apsvcdefs.h"
  13. SC_HANDLE g_hSCM = NULL;
  14. SC_HANDLE g_hAPSvc = NULL;
  15. BOOL g_fIsAPSvcAvailable = FALSE;
  16. DWORD g_dwAPSvcIdleTimeStamp;
  17. #define ESTIMATED_SVC_IDLE_TIMEOUT_IN_SECONDS (((AUTOPROXY_SVC_IDLE_TIMEOUT * 60) * 2) / 3)
  18. BOOL ConnectToAutoProxyService(VOID);
  19. // Return TRUE if the WinHttp Autoproxy Service is available on
  20. // the current platform.
  21. BOOL IsAutoProxyServiceAvailable()
  22. {
  23. if (!GlobalPlatformDotNet)
  24. {
  25. return FALSE;
  26. }
  27. BOOL fRet = FALSE;
  28. if (g_fIsAPSvcAvailable &&
  29. ((::GetTickCount() - g_dwAPSvcIdleTimeStamp) < ESTIMATED_SVC_IDLE_TIMEOUT_IN_SECONDS * 1000))
  30. {
  31. // the svc is marked "loaded" AND we are within the svc idle timtout period, so
  32. // the svc is likey still up & running
  33. fRet = TRUE;
  34. }
  35. else
  36. {
  37. g_fIsAPSvcAvailable = FALSE; // assume the svc is stopped
  38. fRet = ConnectToAutoProxyService();
  39. }
  40. return fRet;
  41. }
  42. DWORD OutProcGetProxyForUrl(
  43. INTERNET_HANDLE_OBJECT* pSession,
  44. LPCWSTR lpcwszUrl,
  45. WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions,
  46. WINHTTP_PROXY_INFO * pProxyInfo
  47. )
  48. {
  49. DWORD dwError = ERROR_SUCCESS;
  50. RPC_STATUS RpcStatus;
  51. RPC_ASYNC_STATE Async;
  52. Async.u.hEvent = NULL;
  53. if (pSession->_hAPBinding == NULL)
  54. {
  55. GeneralInitCritSec.Lock(); // make sure one thread initialize the per-session L-RPC binding handle at a time
  56. if (pSession->_hAPBinding == NULL)
  57. {
  58. LPWSTR pwszBindingString;
  59. RpcStatus = ::RpcStringBindingComposeW(NULL,
  60. AUTOPROXY_L_RPC_PROTOCOL_SEQUENCE,
  61. NULL, // this is a L-RPC service
  62. NULL, // end-point (we are using dynamic endpoints, so this is NULL)
  63. NULL,
  64. &pwszBindingString);
  65. if (RpcStatus != RPC_S_OK)
  66. {
  67. dwError = ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR;
  68. GeneralInitCritSec.Unlock();
  69. goto exit;
  70. }
  71. INET_ASSERT(pwszBindingString != NULL);
  72. RpcStatus = ::RpcBindingFromStringBindingW(pwszBindingString,
  73. &pSession->_hAPBinding);
  74. ::RpcStringFreeW(&pwszBindingString);
  75. if (RpcStatus != RPC_S_OK)
  76. {
  77. dwError = ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR;
  78. GeneralInitCritSec.Unlock();
  79. goto exit;
  80. }
  81. }
  82. GeneralInitCritSec.Unlock();
  83. }
  84. INET_ASSERT(pSession->_hAPBinding != NULL);
  85. RPC_SECURITY_QOS SecQos;
  86. DWORD dwAuthnSvc;
  87. DWORD dwAuthnLevel;
  88. SecQos.Version = RPC_C_SECURITY_QOS_VERSION;
  89. SecQos.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT;
  90. SecQos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC; // id don't change per session
  91. if (pAutoProxyOptions->fAutoLogonIfChallenged)
  92. {
  93. SecQos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
  94. dwAuthnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
  95. dwAuthnSvc = RPC_C_AUTHN_WINNT;
  96. }
  97. else
  98. {
  99. SecQos.ImpersonationType = RPC_C_IMP_LEVEL_ANONYMOUS;
  100. dwAuthnLevel = RPC_C_AUTHN_LEVEL_NONE;
  101. dwAuthnSvc = RPC_C_AUTHN_NONE;
  102. }
  103. RpcStatus = ::RpcBindingSetAuthInfoExW(pSession->_hAPBinding,
  104. NULL, // only needed by kerberos; but we are L-PRC
  105. dwAuthnLevel,
  106. dwAuthnSvc,
  107. NULL,
  108. 0,
  109. &SecQos);
  110. if (RpcStatus != RPC_S_OK)
  111. {
  112. dwError = ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR;
  113. goto exit;
  114. }
  115. RpcStatus = ::RpcAsyncInitializeHandle(&Async, sizeof(RPC_ASYNC_STATE));
  116. if (RpcStatus != RPC_S_OK)
  117. {
  118. dwError = ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR;
  119. goto exit;
  120. }
  121. Async.UserInfo = NULL;
  122. Async.NotificationType = RpcNotificationTypeEvent;
  123. Async.u.hEvent = CreateEvent(NULL,
  124. FALSE, // auto reset
  125. FALSE, // not initially set
  126. NULL);
  127. if (Async.u.hEvent == NULL)
  128. {
  129. dwError = ERROR_NOT_ENOUGH_MEMORY;
  130. goto exit;
  131. }
  132. pAutoProxyOptions->lpvReserved = NULL;
  133. pProxyInfo->dwAccessType = 0;
  134. pProxyInfo->lpszProxy = NULL;
  135. pProxyInfo->lpszProxyBypass = NULL;
  136. SESSION_OPTIONS Timeouts;
  137. DWORD dwTimeout;
  138. pSession->GetTimeout(WINHTTP_OPTION_RESOLVE_TIMEOUT, (LPDWORD)&Timeouts.nResolveTimeout);
  139. pSession->GetTimeout(WINHTTP_OPTION_CONNECT_TIMEOUT, (LPDWORD)&Timeouts.nConnectTimeout);
  140. pSession->GetTimeout(WINHTTP_OPTION_CONNECT_RETRIES, (LPDWORD)&Timeouts.nConnectRetries);
  141. pSession->GetTimeout(WINHTTP_OPTION_SEND_TIMEOUT, (LPDWORD)&Timeouts.nSendTimeout);
  142. pSession->GetTimeout(WINHTTP_OPTION_RECEIVE_TIMEOUT, (LPDWORD)&Timeouts.nReceiveTimeout);
  143. RpcTryExcept
  144. {
  145. // ClientGetProxyForUrl is the MIDL-generated client stub function; the server stub
  146. // is called GetProxyForUrl. Since both RPC client & server stub is in the same DLL,
  147. // we used the -prefix MIDL switch to prepend "Client" to the client stub to avoid
  148. // name collisions
  149. ClientGetProxyForUrl(&Async,
  150. pSession->_hAPBinding,
  151. lpcwszUrl,
  152. (P_AUTOPROXY_OPTIONS)pAutoProxyOptions,
  153. &Timeouts,
  154. (P_AUTOPROXY_RESULT)pProxyInfo,
  155. &dwError);
  156. }
  157. RpcExcept(1)
  158. {
  159. if (::RpcExceptionCode() == EPT_S_NOT_REGISTERED)
  160. {
  161. // we thought the svc is available because the idle timeout hasn't expired yet but
  162. // we got an exception here saying the L-RPC endpoint isn't available. So someone
  163. // much have stopped the service manually.
  164. g_fIsAPSvcAvailable = FALSE;
  165. }
  166. dwError = ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR;
  167. goto exit;
  168. }
  169. RpcEndExcept
  170. if ((Timeouts.nResolveTimeout == INFINITE) ||
  171. (Timeouts.nConnectTimeout == INFINITE) ||
  172. (Timeouts.nSendTimeout == INFINITE) ||
  173. (Timeouts.nReceiveTimeout == INFINITE))
  174. {
  175. dwTimeout = INFINITE;
  176. }
  177. else
  178. {
  179. dwTimeout = Timeouts.nResolveTimeout +
  180. Timeouts.nConnectTimeout +
  181. Timeouts.nSendTimeout +
  182. Timeouts.nReceiveTimeout;
  183. }
  184. DWORD dwWaitResult;
  185. DWORD dwWaitTime = 0;
  186. // we are going to wait for the result. But we won't wait for it more than half a sec.
  187. // at a time, so that app can cancel this API call. the minimum wait is half a sec. Also
  188. // we won't wait longer than the app specified time-out.
  189. if (dwTimeout < 500)
  190. {
  191. dwTimeout = 500;
  192. }
  193. do
  194. {
  195. dwWaitResult = ::WaitForSingleObject(Async.u.hEvent, 500);
  196. if (dwWaitResult == WAIT_OBJECT_0)
  197. {
  198. break;
  199. }
  200. if (pSession->IsInvalidated())
  201. {
  202. dwError = ERROR_WINHTTP_OPERATION_CANCELLED;
  203. break;
  204. }
  205. dwWaitTime += 500;
  206. } while (dwWaitTime < dwTimeout);
  207. if (dwWaitResult != WAIT_OBJECT_0)
  208. {
  209. (void)::RpcAsyncCancelCall(&Async, TRUE); // cancel immediately
  210. dwError = ERROR_WINHTTP_TIMEOUT;
  211. goto exit;
  212. }
  213. // result has come back
  214. BOOL fRet;
  215. RpcStatus = ::RpcAsyncCompleteCall(&Async, &fRet);
  216. if (RpcStatus != RPC_S_OK)
  217. {
  218. dwError = ((RpcStatus == RPC_S_CALL_CANCELLED) ? ERROR_WINHTTP_OPERATION_CANCELLED : ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR);
  219. goto exit;
  220. }
  221. if (fRet == FALSE)
  222. {
  223. // dwError should be updated by the RPC call itself
  224. goto exit;
  225. }
  226. exit:
  227. if (Async.u.hEvent)
  228. {
  229. ::CloseHandle(Async.u.hEvent);
  230. }
  231. return dwError;
  232. }
  233. VOID AutoProxySvcDetach(VOID)
  234. {
  235. if (g_hAPSvc)
  236. {
  237. ::CloseServiceHandle(g_hAPSvc);
  238. g_hAPSvc = NULL;
  239. }
  240. if (g_hSCM)
  241. {
  242. ::CloseServiceHandle(g_hSCM);
  243. g_hSCM = NULL;
  244. }
  245. }
  246. BOOL ConnectToAutoProxyService(VOID)
  247. {
  248. // this function is not thread safe, caller must assure only one thread can call
  249. // this function at a time.
  250. if (g_hAPSvc == NULL)
  251. {
  252. GeneralInitCritSec.Lock();
  253. if (g_hAPSvc == NULL)
  254. {
  255. if (g_hSCM == NULL)
  256. {
  257. g_hSCM = ::OpenSCManagerW(NULL, // Local Computer for L-RPC accesss
  258. NULL, // default SCM DB
  259. SC_MANAGER_CONNECT);
  260. if (g_hSCM == NULL)
  261. {
  262. GeneralInitCritSec.Unlock();
  263. return FALSE;
  264. }
  265. }
  266. g_hAPSvc = ::OpenServiceW(g_hSCM,
  267. WINHTTP_AUTOPROXY_SERVICE_NAME,
  268. SERVICE_START |
  269. SERVICE_QUERY_STATUS |
  270. SERVICE_INTERROGATE);
  271. if (g_hAPSvc == NULL)
  272. {
  273. GeneralInitCritSec.Unlock();
  274. return FALSE;
  275. }
  276. }
  277. GeneralInitCritSec.Unlock();
  278. }
  279. INET_ASSERT(g_hAPSvc != NULL);
  280. BOOL fRet = FALSE;
  281. GeneralInitCritSec.Lock(); // one thread to init at a time
  282. if (!g_fIsAPSvcAvailable)
  283. {
  284. SERVICE_STATUS SvcStatus;
  285. if (::QueryServiceStatus(g_hAPSvc, &SvcStatus) == FALSE)
  286. {
  287. goto exit;
  288. }
  289. if (SvcStatus.dwCurrentState == SERVICE_RUNNING ||
  290. SvcStatus.dwCurrentState == SERVICE_STOP_PENDING // there is a possibility that the service failed to shutdown gracefully
  291. // and it's stucked in the STOP_PENDING state. In that case, however, the
  292. // L-RPC service will continue be available
  293. )
  294. {
  295. g_dwAPSvcIdleTimeStamp = ::GetTickCount();
  296. g_fIsAPSvcAvailable = TRUE;
  297. fRet = TRUE;
  298. goto exit;
  299. }
  300. if (SvcStatus.dwCurrentState == SERVICE_STOPPED)
  301. {
  302. if (::StartServiceW(g_hAPSvc, 0, NULL) == FALSE)
  303. {
  304. DWORD dwError = ::GetLastError();
  305. if (dwError == ERROR_SERVICE_ALREADY_RUNNING)
  306. {
  307. g_dwAPSvcIdleTimeStamp = ::GetTickCount();
  308. g_fIsAPSvcAvailable = TRUE;
  309. fRet = TRUE;
  310. }
  311. goto exit;
  312. }
  313. }
  314. else if (SvcStatus.dwCurrentState != SERVICE_START_PENDING)
  315. {
  316. goto exit;
  317. }
  318. // at this point either 1) WinHttp.dll is starting the Svc, or 2
  319. // the SVC is being started otuside WinHttp.dll (e.g. admin starting it using SCM)
  320. // either case we just need to wait for the Svc to run
  321. // the code below is based on an MSDN sample
  322. //wait for service to complete init
  323. if (::QueryServiceStatus(g_hAPSvc, &SvcStatus) == FALSE)
  324. {
  325. goto exit;
  326. }
  327. // Save the tick count and initial checkpoint.
  328. DWORD dwStartTickCount = GetTickCount();
  329. DWORD dwOldCheckPoint = SvcStatus.dwCheckPoint;
  330. while (SvcStatus.dwCurrentState == SERVICE_START_PENDING)
  331. {
  332. // Do not wait longer than the wait hint. A good interval is
  333. // one tenth the wait hint, but no less than 1 second and no
  334. // more than 10 seconds.
  335. //DWORD dwWaitTime = SvcStatus.dwWaitHint / 10;
  336. //if (dwWaitTime < 1000)
  337. // dwWaitTime = 1000;
  338. //else if (dwWaitTime > 10000)
  339. // dwWaitTime = 10000;
  340. Sleep(250);
  341. if (::QueryServiceStatus(g_hAPSvc, &SvcStatus) == FALSE)
  342. {
  343. goto exit;
  344. }
  345. if (SvcStatus.dwCheckPoint > dwOldCheckPoint)
  346. {
  347. // The service is making progress.
  348. dwStartTickCount = GetTickCount();
  349. dwOldCheckPoint = SvcStatus.dwCheckPoint;
  350. }
  351. else
  352. {
  353. if(GetTickCount() - dwStartTickCount > SvcStatus.dwWaitHint)
  354. {
  355. break;
  356. }
  357. }
  358. }
  359. if (SvcStatus.dwCurrentState != SERVICE_RUNNING)
  360. {
  361. goto exit;
  362. }
  363. }
  364. g_dwAPSvcIdleTimeStamp = ::GetTickCount();
  365. g_fIsAPSvcAvailable = TRUE;
  366. fRet = TRUE;
  367. exit:
  368. GeneralInitCritSec.Unlock();
  369. return fRet;
  370. }