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.

505 lines
15 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. context.c
  5. Abstract:
  6. This module contains functions to manage contexts for TFTP
  7. sessions with remote clients.
  8. Author:
  9. Jeffrey C. Venable, Sr. (jeffv) 01-Jun-2001
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. void
  14. TftpdContextLeak(PTFTPD_CONTEXT context) {
  15. PLIST_ENTRY entry;
  16. EnterCriticalSection(&globals.reaper.contextCS); {
  17. // If shutdown is occuring, we're in trouble anyways. Just let it go.
  18. if (globals.service.shutdown) {
  19. LeaveCriticalSection(&globals.reaper.contextCS);
  20. if (InterlockedDecrement(&globals.io.numContexts) == -1)
  21. TftpdServiceAttemptCleanup();
  22. return;
  23. }
  24. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT, "TftpdContextLeak(context = %p).\n", context));
  25. // Is the context already in the list?
  26. for (entry = globals.reaper.leakedContexts.Flink;
  27. entry != &globals.reaper.leakedContexts;
  28. entry = entry->Flink) {
  29. if (CONTAINING_RECORD(entry, TFTPD_CONTEXT, linkage) == context) {
  30. LeaveCriticalSection(&globals.reaper.contextCS);
  31. return;
  32. }
  33. }
  34. InsertHeadList(&globals.reaper.leakedContexts, &context->linkage);
  35. globals.reaper.numLeakedContexts++;
  36. TftpdContextAddReference(context);
  37. } LeaveCriticalSection(&globals.reaper.contextCS);
  38. } // TftpdContextLeak()
  39. BOOL
  40. TftpdContextFree(
  41. PTFTPD_CONTEXT context
  42. );
  43. void CALLBACK
  44. TftpdContextTimerCleanup(PTFTPD_CONTEXT context, BOOLEAN timeout) {
  45. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT,
  46. "TftpdContextTimerCleanup(context = %p).\n",
  47. context));
  48. context->hTimer = NULL;
  49. if (!UnregisterWait(context->wWait)) {
  50. DWORD error;
  51. if ((error = GetLastError()) != ERROR_IO_PENDING) {
  52. TFTPD_DEBUG((TFTPD_DBG_CONTEXT,
  53. "TftpdContextTimerCleanup(context = %p): "
  54. "UnregisterWait() failed, error 0x%08X.\n",
  55. context,
  56. error));
  57. TftpdContextLeak(context);
  58. return;
  59. }
  60. }
  61. context->wWait = NULL;
  62. TftpdContextFree(context);
  63. } // TftpdContextTimerCleanup()
  64. BOOL
  65. TftpdContextFree(PTFTPD_CONTEXT context) {
  66. DWORD numContexts;
  67. NTSTATUS status;
  68. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT,
  69. "TftpdContextFree(context = %p).\n",
  70. context));
  71. if (context->wWait != NULL) {
  72. if (!UnregisterWait(context->wWait)) {
  73. DWORD error;
  74. if ((error = GetLastError()) != ERROR_IO_PENDING) {
  75. TFTPD_DEBUG((TFTPD_DBG_CONTEXT,
  76. "TftpdContextFree(context = %p): "
  77. "UnregisterWait() failed, error 0x%08X.\n",
  78. context,
  79. error));
  80. TftpdContextLeak(context);
  81. return (FALSE);
  82. }
  83. }
  84. context->wWait = NULL;
  85. }
  86. if (context->hTimer != NULL) {
  87. HANDLE hTimer;
  88. BOOL reset;
  89. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT,
  90. "TftpdContextFree(context = %p): "
  91. "Deleting timer.\n",
  92. context));
  93. // WriteFile() or ReadFile() may have signalled this event if they
  94. // last completed immediately.
  95. reset = ResetEvent(context->hWait);
  96. ASSERT(reset);
  97. ASSERT(context->wWait == NULL);
  98. if (!RegisterWaitForSingleObject(&context->wWait,
  99. context->hWait,
  100. (WAITORTIMERCALLBACKFUNC)TftpdContextTimerCleanup,
  101. context,
  102. INFINITE,
  103. (WT_EXECUTEINIOTHREAD | WT_EXECUTEONLYONCE))) {
  104. TFTPD_DEBUG((TFTPD_DBG_CONTEXT,
  105. "TftpdContextFree(context = %p): "
  106. "RegisterWaitForSingleObject() failed, error 0x%08X.\n",
  107. context, GetLastError()));
  108. TftpdContextLeak(context);
  109. return (FALSE);
  110. }
  111. if (!DeleteTimerQueueTimer(globals.io.hTimerQueue,
  112. context->hTimer,
  113. context->hWait)) {
  114. DWORD error;
  115. if ((error = GetLastError()) != ERROR_IO_PENDING) {
  116. TFTPD_DEBUG((TFTPD_DBG_CONTEXT,
  117. "TftpdContextFree(context = %p): "
  118. "DeleteTimerQueueTimer() failed, error 0x%08X.\n",
  119. context,
  120. error));
  121. // The next call to TftpdContextFree() to recover this context from the
  122. // leak list will deregister the wait for us.
  123. TftpdContextLeak(context);
  124. return (FALSE);
  125. }
  126. }
  127. return (TRUE);
  128. } // if (context->hTimer != NULL)
  129. ASSERT(context->wWait == NULL);
  130. // If a private socket was used, destroy it.
  131. if ((context->socket != NULL) && context->socket->context)
  132. TftpdIoDestroySocketContext(context->socket);
  133. // Cleanup everything else.
  134. if (context->hFile != NULL)
  135. CloseHandle(context->hFile);
  136. if (context->hWait != NULL)
  137. CloseHandle(context->hWait);
  138. if (context->filename != NULL)
  139. HeapFree(globals.hServiceHeap, 0, context->filename);
  140. numContexts = InterlockedDecrement(&globals.io.numContexts);
  141. HeapFree(globals.hServiceHeap, 0, context);
  142. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT,
  143. "TftpdContextFree(context = %p): ### numContexts = %d.\n",
  144. context, numContexts));
  145. if (numContexts == -1)
  146. TftpdServiceAttemptCleanup();
  147. return (TRUE);
  148. } // TftpdContextFree()
  149. DWORD
  150. TftpdContextAddReference(PTFTPD_CONTEXT context) {
  151. DWORD result;
  152. result = InterlockedIncrement(&context->reference);
  153. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT,
  154. "TftpdContextAddReference(context = %p): reference = %d.\n",
  155. context, result));
  156. return (result);
  157. } // TftpdContextAddReference()
  158. PTFTPD_CONTEXT
  159. TftpdContextAllocate() {
  160. PTFTPD_CONTEXT context = NULL;
  161. DWORD numContexts;
  162. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT, "TftpdContextAllocate().\n"));
  163. if (globals.reaper.leakedContexts.Flink != &globals.reaper.leakedContexts) {
  164. BOOL failAllocate = FALSE;
  165. // Try to recover leaked contexts.
  166. EnterCriticalSection(&globals.reaper.contextCS); {
  167. PLIST_ENTRY entry;
  168. while ((entry = RemoveHeadList(&globals.reaper.leakedContexts)) !=
  169. &globals.reaper.leakedContexts) {
  170. globals.reaper.numLeakedContexts--;
  171. if (!TftpdContextFree(CONTAINING_RECORD(entry, TFTPD_CONTEXT, linkage))) {
  172. // If the free failed, the context is readded to the leak list.
  173. // Free the reference from it having already been on the leak list.
  174. TftpdContextRelease(context);
  175. failAllocate = TRUE;
  176. break;
  177. }
  178. }
  179. } LeaveCriticalSection(&globals.reaper.contextCS);
  180. if (failAllocate)
  181. goto exit_allocate_context;
  182. } // if (globals.reaper.leakedContexts.Flink != &globals.reaper.leakedContexts)
  183. context = (PTFTPD_CONTEXT)HeapAlloc(globals.hServiceHeap, HEAP_ZERO_MEMORY, sizeof(TFTPD_CONTEXT));
  184. if (context == NULL) {
  185. TFTPD_DEBUG((TFTPD_DBG_CONTEXT,
  186. "TftpdContextAllocate(): HeapAlloc() failed, error = 0x%08X.\n",
  187. GetLastError()));
  188. return (NULL);
  189. }
  190. InitializeListHead(&context->linkage);
  191. context->sorcerer = -1;
  192. numContexts = InterlockedIncrement(&globals.io.numContexts);
  193. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT, "TftpdContextAllocate(): ### numContexts = %d.\n", numContexts));
  194. if (globals.service.shutdown)
  195. TftpdContextFree(context), context = NULL;
  196. exit_allocate_context :
  197. return (context);
  198. } // TftpdContextAllocate()
  199. DWORD
  200. TftpdContextHash(PSOCKADDR_IN addr) {
  201. return ((addr->sin_addr.s_addr + addr->sin_port) % globals.parameters.hashEntries);
  202. } // TftpdContextHash()
  203. BOOL
  204. TftpdContextAdd(PTFTPD_CONTEXT context) {
  205. PLIST_ENTRY entry;
  206. DWORD index;
  207. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT, "TftpdContextAdd(context = %p).\n", context));
  208. index = TftpdContextHash(&context->peer);
  209. EnterCriticalSection(&globals.hash.table[index].cs); {
  210. if (globals.service.shutdown) {
  211. LeaveCriticalSection(&globals.hash.table[index].cs);
  212. return (FALSE);
  213. }
  214. // Is the context already in the table?
  215. for (entry = globals.hash.table[index].bucket.Flink;
  216. entry != &globals.hash.table[index].bucket;
  217. entry = entry->Flink) {
  218. PTFTPD_CONTEXT c = CONTAINING_RECORD(entry, TFTPD_CONTEXT, linkage);
  219. if ((c->peer.sin_addr.s_addr == context->peer.sin_addr.s_addr) &&
  220. (c->peer.sin_port == context->peer.sin_port)) {
  221. TFTPD_DEBUG((TFTPD_DBG_CONTEXT,
  222. "TftpdContextAdd(context = %p): TID already exists.\n",
  223. context));
  224. LeaveCriticalSection(&globals.hash.table[index].cs);
  225. return (FALSE);
  226. }
  227. }
  228. TftpdContextAddReference(context);
  229. InsertHeadList(&globals.hash.table[index].bucket, &context->linkage);
  230. #if defined(DBG)
  231. {
  232. DWORD numEntries, maxClients;
  233. numEntries = InterlockedIncrement((PLONG)&globals.hash.numEntries);
  234. InterlockedIncrement((PLONG)&globals.hash.table[index].numEntries);
  235. while (numEntries > (maxClients = globals.performance.maxClients))
  236. InterlockedCompareExchange((PLONG)&globals.performance.maxClients, numEntries, maxClients);
  237. }
  238. #endif // defined(DBG)
  239. } LeaveCriticalSection(&globals.hash.table[index].cs);
  240. return (TRUE);
  241. } // TftpdContextAdd()
  242. void
  243. TftpdContextRemove(PTFTPD_CONTEXT context) {
  244. PLIST_ENTRY entry;
  245. DWORD index;
  246. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT, "TftpdContextRemove(context = %p).\n", context));
  247. index = TftpdContextHash(&context->peer);
  248. EnterCriticalSection(&globals.hash.table[index].cs); {
  249. // Validate that the context is still in the bucket and
  250. // wasn't already removed by another thread.
  251. for (entry = globals.hash.table[index].bucket.Flink;
  252. entry != &globals.hash.table[index].bucket;
  253. entry = entry->Flink) {
  254. PTFTPD_CONTEXT c;
  255. c = CONTAINING_RECORD(entry, TFTPD_CONTEXT, linkage);
  256. if (c == context) {
  257. // Pull the context out of the hash-table.
  258. RemoveEntryList(&context->linkage);
  259. TftpdContextRelease(context);
  260. #if defined(DBG)
  261. InterlockedDecrement((PLONG)&globals.hash.numEntries);
  262. InterlockedDecrement((PLONG)&globals.hash.table[index].numEntries);
  263. #endif // defined(DBG)
  264. break;
  265. } // if (c == context)
  266. }
  267. } LeaveCriticalSection(&globals.hash.table[index].cs);
  268. } // TftpdContextRemove()
  269. void
  270. TftpdContextKill(PTFTPD_CONTEXT context) {
  271. // Set the dead flag in the context state.
  272. while (TRUE) {
  273. DWORD state = context->state;
  274. if (state & TFTPD_STATE_DEAD)
  275. return;
  276. if (InterlockedCompareExchange(&context->state, (state | TFTPD_STATE_DEAD), state) == state)
  277. break;
  278. }
  279. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT, "TftpdContextKill(context = %p).\n", context));
  280. // Add a reference count to the context for ourselves so it won't free
  281. // itself from under us as we close the file below.
  282. TftpdContextAddReference(context);
  283. // Remove it from the hash-table.
  284. TftpdContextRemove(context);
  285. // Close the file. This will force any outstanding overlapped read or write operations
  286. // to complete immediately, deregister their waits, and decrement their reference
  287. // to this context.
  288. if (context->hFile != NULL) {
  289. CloseHandle(context->hFile);
  290. context->hFile = NULL;
  291. }
  292. // Release our kill reference.
  293. TftpdContextRelease(context);
  294. } // TftpdContextKill()
  295. BOOL
  296. TftpdContextUpdateTimer(PTFTPD_CONTEXT context) {
  297. ULONG timeout = context->timeout;
  298. ASSERT(context->state & TFTPD_STATE_BUSY);
  299. if (!timeout) {
  300. unsigned int x;
  301. timeout = 1000;
  302. for (x = 0; x < context->retransmissions; x++)
  303. timeout *= 2;
  304. if (timeout > 10000)
  305. timeout = 10000;
  306. }
  307. // Update the retransmission timer.
  308. return (ChangeTimerQueueTimer(globals.io.hTimerQueue, context->hTimer, timeout, 720000));
  309. } // TftpdContextUpdateTimer()
  310. PTFTPD_CONTEXT
  311. TftpdContextAquire(PSOCKADDR_IN addr) {
  312. PTFTPD_CONTEXT context = NULL;
  313. PLIST_ENTRY entry;
  314. DWORD index;
  315. if (globals.service.shutdown)
  316. goto exit_acquire;
  317. index = TftpdContextHash(addr);
  318. EnterCriticalSection(&globals.hash.table[index].cs); {
  319. if (!globals.service.shutdown) {
  320. for (entry = globals.hash.table[index].bucket.Flink;
  321. entry != &globals.hash.table[index].bucket;
  322. entry = entry->Flink) {
  323. PTFTPD_CONTEXT c;
  324. c = CONTAINING_RECORD(entry, TFTPD_CONTEXT, linkage);
  325. if ((c->peer.sin_addr.s_addr == addr->sin_addr.s_addr) &&
  326. (c->peer.sin_port == addr->sin_port)) {
  327. context = c;
  328. TftpdContextAddReference(context);
  329. break;
  330. }
  331. }
  332. } // if (!globals.service.shutdown)
  333. } LeaveCriticalSection(&globals.hash.table[index].cs);
  334. if ((context != NULL) && (context->state & TFTPD_STATE_DEAD)) {
  335. TftpdContextRelease(context);
  336. context = NULL;
  337. }
  338. exit_acquire :
  339. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT,
  340. "TftpdContextAquire(TID = %s:%d): context = %p.\n",
  341. inet_ntoa(addr->sin_addr), addr->sin_port, context));
  342. return (context);
  343. } // TftpdContextAquire()
  344. DWORD
  345. TftpdContextRelease(PTFTPD_CONTEXT context) {
  346. DWORD reference;
  347. TFTPD_DEBUG((TFTPD_TRACE_CONTEXT, "TftpdContextRelease(context = %p).\n", context));
  348. // When a context is killable, only its retransmit timer will have a reference to it.
  349. reference = InterlockedDecrement(&context->reference);
  350. if (reference == 0)
  351. TftpdContextFree(context);
  352. return (reference);
  353. } // TftpdContextRelease()