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.

561 lines
15 KiB

  1. /*++
  2. Copyright (c) 1999, Microsoft Corporation
  3. Module Name:
  4. portapi.c
  5. Abstract:
  6. This module contains code for API routines which provide port-reservation
  7. functionality to user-mode clients of TCP/IP. This functionality allows
  8. applications to 'reserve' blocks of TCP/UDP port-numbers for private use,
  9. preventing any other processes from binding to the reserved port-numbers.
  10. Author:
  11. Abolade Gbadegesin (aboladeg) 25-May-1999
  12. Revision History:
  13. --*/
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. #include <ipnatapi.h>
  17. #include <ntddtcp.h>
  18. //
  19. // PRIVATE STRUCTURE DECLARATIONS
  20. //
  21. typedef struct _NAT_PORT_RESERVATION {
  22. CRITICAL_SECTION Lock;
  23. HANDLE TcpipHandle;
  24. USHORT BlockSize;
  25. USHORT PortBlockSize;
  26. LIST_ENTRY PortBlockList;
  27. } NAT_PORT_RESERVATION, *PNAT_PORT_RESERVATION;
  28. typedef struct _NAT_PORT_BLOCK {
  29. LIST_ENTRY Link;
  30. ULONG StartHandle;
  31. RTL_BITMAP Bitmap;
  32. ULONG BitmapBuffer[0];
  33. } NAT_PORT_BLOCK, *PNAT_PORT_BLOCK;
  34. //
  35. // FORWARD DECLARATIONS
  36. //
  37. ULONG
  38. NatpCreatePortBlock(
  39. PNAT_PORT_RESERVATION PortReservation,
  40. PNAT_PORT_BLOCK* PortBlockCreated
  41. );
  42. VOID
  43. NatpDeletePortBlock(
  44. PNAT_PORT_RESERVATION PortReservation,
  45. PNAT_PORT_BLOCK PortBlock
  46. );
  47. ULONG
  48. NatAcquirePortReservation(
  49. HANDLE ReservationHandle,
  50. USHORT PortCount,
  51. OUT PUSHORT ReservedPortBase
  52. )
  53. /*++
  54. Routine Description:
  55. This routine is called to reserve one or more contiguous port-numbers
  56. from the port-reservation handle supplied.
  57. Arguments:
  58. ReservationHandle - supplies a port-reservation handle from which to
  59. acquire port-numbers
  60. PortCount - specifies the number of port-numbers required
  61. ReservedPortBase - receives the first port-number reserved,
  62. in network-order.
  63. Return Value:
  64. ULONG - Win32 status code.
  65. --*/
  66. {
  67. ULONG Error;
  68. ULONG Index;
  69. PLIST_ENTRY Link;
  70. PNAT_PORT_BLOCK PortBlock;
  71. PNAT_PORT_RESERVATION PortReservation =
  72. (PNAT_PORT_RESERVATION)ReservationHandle;
  73. NTSTATUS Status;
  74. //
  75. // Fail immediately if the caller has requested more port-numbers
  76. // than would exist in a completely unallocated block.
  77. // Otherwise, traverse the list of port-blocks to see if any of the blocks
  78. // have enough contiguous port-numbers to satisfy the caller's request.
  79. //
  80. if (PortCount > PortReservation->BlockSize) {
  81. return ERROR_INVALID_PARAMETER;
  82. }
  83. EnterCriticalSection(&PortReservation->Lock);
  84. for (Link = PortReservation->PortBlockList.Flink;
  85. Link != &PortReservation->PortBlockList; Link = Link->Flink) {
  86. PortBlock = CONTAINING_RECORD(Link, NAT_PORT_BLOCK, Link);
  87. Index = RtlFindClearBitsAndSet(&PortBlock->Bitmap, PortCount, 0);
  88. if (Index != (ULONG)-1) {
  89. *ReservedPortBase =
  90. RtlUshortByteSwap((USHORT)(PortBlock->StartHandle + Index));
  91. LeaveCriticalSection(&PortReservation->Lock);
  92. return NO_ERROR;
  93. }
  94. }
  95. //
  96. // No port-block had the required number of contiguous port-numbers.
  97. // Attempt to create a new port-block, and if that succeeds use it
  98. // to satisfy the caller's request.
  99. //
  100. Error = NatpCreatePortBlock(PortReservation, &PortBlock);
  101. if (NO_ERROR != Error) {
  102. LeaveCriticalSection(&PortReservation->Lock);
  103. return Error;
  104. }
  105. Index = RtlFindClearBitsAndSet(&PortBlock->Bitmap, PortCount, 0);
  106. *ReservedPortBase =
  107. RtlUshortByteSwap((USHORT)(PortBlock->StartHandle + Index));
  108. LeaveCriticalSection(&PortReservation->Lock);
  109. return NO_ERROR;
  110. } // NatAcquirePortReservation
  111. ULONG
  112. NatInitializePortReservation(
  113. USHORT BlockSize,
  114. OUT PHANDLE ReservationHandle
  115. )
  116. /*++
  117. Routine Description:
  118. This routine is called to initialize a handle to the port-reservation
  119. module. The resulting handle is used to acquire and release ports
  120. from the dynamically-allocated block.
  121. Arguments:
  122. BlockSize - indicates the number of ports to request each time
  123. an additional block is requested from the TCP/IP driver.
  124. ReservationHandle - on output, receives a handle to be used for
  125. acquiring and releasing ports.
  126. Return Value:
  127. ULONG - Win32 status code.
  128. --*/
  129. {
  130. ULONG BitmapSize;
  131. IO_STATUS_BLOCK IoStatus;
  132. OBJECT_ATTRIBUTES ObjectAttributes;
  133. PNAT_PORT_RESERVATION PortReservation;
  134. NTSTATUS Status;
  135. HANDLE TcpipHandle;
  136. UNICODE_STRING UnicodeString;
  137. do {
  138. //
  139. // Open a handle to the TCP/IP driver.
  140. // This handle will later be used to issue reservation-requests.
  141. //
  142. RtlInitUnicodeString(&UnicodeString, DD_TCP_DEVICE_NAME);
  143. InitializeObjectAttributes(
  144. &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL
  145. );
  146. Status =
  147. NtCreateFile(
  148. &TcpipHandle,
  149. SYNCHRONIZE|FILE_READ_DATA|FILE_WRITE_DATA,
  150. &ObjectAttributes,
  151. &IoStatus,
  152. NULL,
  153. FILE_ATTRIBUTE_NORMAL,
  154. FILE_SHARE_READ|FILE_SHARE_WRITE,
  155. FILE_OPEN_IF,
  156. 0,
  157. NULL,
  158. 0
  159. );
  160. if (!NT_SUCCESS(Status)) { break; }
  161. //
  162. // Allocate and initialize a port-reservation context block.
  163. //
  164. PortReservation = MALLOC(sizeof(*PortReservation));
  165. if (!PortReservation) { Status = STATUS_NO_MEMORY; break; }
  166. if (FALSE == InitializeCriticalSectionAndSpinCount(&PortReservation->Lock, 0)) {
  167. Status = STATUS_NO_MEMORY;
  168. break;
  169. }
  170. PortReservation->TcpipHandle = TcpipHandle;
  171. PortReservation->BlockSize = BlockSize;
  172. BitmapSize = (BlockSize + sizeof(ULONG) * 8 - 1) / (sizeof(ULONG) * 8);
  173. PortReservation->PortBlockSize =
  174. (USHORT)FIELD_OFFSET(NAT_PORT_BLOCK, BitmapBuffer[BitmapSize]);
  175. InitializeListHead(&PortReservation->PortBlockList);
  176. *ReservationHandle = (HANDLE)PortReservation;
  177. return NO_ERROR;
  178. } while(FALSE);
  179. if (TcpipHandle) { NtClose(TcpipHandle); }
  180. if (PortReservation) { FREE(PortReservation); }
  181. return RtlNtStatusToDosError(Status);
  182. } // NatInitializePortReservation
  183. ULONG
  184. NatpCreatePortBlock(
  185. PNAT_PORT_RESERVATION PortReservation,
  186. PNAT_PORT_BLOCK* PortBlockCreated
  187. )
  188. /*++
  189. Routine Description:
  190. This routine is called to create a new port-block when the existing
  191. port-numbers have been exhausted.
  192. Arguments:
  193. PortReservation - the reservation to which the port-block should be added
  194. PortBlockCreated - on output, receives the new port-block
  195. Return Value:
  196. ULONG - Win32 error code; return NO_ERROR if successful.
  197. Environment:
  198. PortReservation->Lock must be held by the caller.
  199. --*/
  200. {
  201. IO_STATUS_BLOCK IoStatus;
  202. PLIST_ENTRY Link;
  203. PNAT_PORT_BLOCK PortBlock;
  204. TCP_BLOCKPORTS_REQUEST Request;
  205. ULONG StartHandle;
  206. NTSTATUS Status;
  207. HANDLE WaitEvent;
  208. //
  209. // Allocate memory for the new port-block and its bitmap of free ports
  210. //
  211. PortBlock = MALLOC(PortReservation->PortBlockSize);
  212. if (!PortBlock) { return ERROR_NOT_ENOUGH_MEMORY; }
  213. //
  214. // Request a new block of ports from the TCP/IP driver
  215. //
  216. WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  217. if (WaitEvent == NULL) {
  218. FREE(PortBlock);
  219. return ERROR_NOT_ENOUGH_MEMORY;
  220. }
  221. Request.ReservePorts = TRUE;
  222. Request.NumberofPorts = PortReservation->BlockSize;
  223. Status =
  224. NtDeviceIoControlFile(
  225. PortReservation->TcpipHandle,
  226. WaitEvent,
  227. NULL,
  228. NULL,
  229. &IoStatus,
  230. IOCTL_TCP_BLOCK_PORTS,
  231. &Request,
  232. sizeof(Request),
  233. &StartHandle,
  234. sizeof(StartHandle)
  235. );
  236. if (Status == STATUS_PENDING) {
  237. WaitForSingleObject(WaitEvent, INFINITE);
  238. Status = IoStatus.Status;
  239. }
  240. CloseHandle(WaitEvent);
  241. if (!NT_SUCCESS(Status)) {
  242. FREE(PortBlock); return RtlNtStatusToDosError(Status);
  243. }
  244. //
  245. // Initialize the new port-block, and insert it in the list of ports.
  246. //
  247. PortBlock->StartHandle = StartHandle;
  248. RtlInitializeBitMap(
  249. &PortBlock->Bitmap,
  250. PortBlock->BitmapBuffer,
  251. PortReservation->BlockSize
  252. );
  253. RtlClearAllBits(&PortBlock->Bitmap);
  254. for (Link = PortReservation->PortBlockList.Flink;
  255. Link != &PortReservation->PortBlockList; Link = Link->Flink) {
  256. PNAT_PORT_BLOCK Temp = CONTAINING_RECORD(Link, NAT_PORT_BLOCK, Link);
  257. if (PortBlock->StartHandle > Temp->StartHandle) {
  258. continue;
  259. } else {
  260. break;
  261. }
  262. ASSERTMSG("NatpCreatePortBlock: duplicate port range\n", TRUE);
  263. }
  264. InsertTailList(Link, &PortBlock->Link);
  265. if (PortBlockCreated) { *PortBlockCreated = PortBlock; }
  266. return NO_ERROR;
  267. } // NatpCreatePortBlock
  268. VOID
  269. NatpDeletePortBlock(
  270. PNAT_PORT_RESERVATION PortReservation,
  271. PNAT_PORT_BLOCK PortBlock
  272. )
  273. /*++
  274. Routine Description:
  275. This routine is called to delete a port-block when the port-numbers
  276. it contains have been released, or when the port-reservation is cleaned up.
  277. Arguments:
  278. PortReservation - the reservation to which the port-block belongs
  279. PortBlock - the port block to be deleted
  280. Return Value:
  281. none.
  282. Environment:
  283. PortReservation->Lock must be held by the caller.
  284. --*/
  285. {
  286. IO_STATUS_BLOCK IoStatus;
  287. TCP_BLOCKPORTS_REQUEST Request;
  288. NTSTATUS Status;
  289. HANDLE WaitEvent;
  290. WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  291. if (WaitEvent == NULL) {
  292. return;
  293. }
  294. //
  295. // Release the block of ports to the TCP/IP driver
  296. //
  297. Request.ReservePorts = FALSE;
  298. Request.StartHandle = PortBlock->StartHandle;
  299. Status =
  300. NtDeviceIoControlFile(
  301. PortReservation->TcpipHandle,
  302. WaitEvent,
  303. NULL,
  304. NULL,
  305. &IoStatus,
  306. IOCTL_TCP_BLOCK_PORTS,
  307. &Request,
  308. sizeof(Request),
  309. NULL,
  310. 0
  311. );
  312. if (Status == STATUS_PENDING) {
  313. WaitForSingleObject(WaitEvent, INFINITE);
  314. Status = IoStatus.Status;
  315. }
  316. RemoveEntryList(&PortBlock->Link);
  317. FREE(PortBlock);
  318. CloseHandle(WaitEvent);
  319. } // NatpDeletePortBlock
  320. ULONG
  321. NatReleasePortReservation(
  322. HANDLE ReservationHandle,
  323. USHORT ReservedPortBase,
  324. USHORT PortCount
  325. )
  326. /*++
  327. Routine Description:
  328. This routine is called to release all contiguous port-numbers obtained
  329. in a previous acquisition from the port-reservation handle supplied.
  330. Arguments:
  331. ReservationHandle - supplies a port-reservation handle to which to
  332. release port-numbers
  333. ReservedPortBase - receives the first port-number reserved,
  334. in network-order.
  335. PortCount - specifies the number of port-numbers acquired
  336. Return Value:
  337. ULONG - Win32 status code.
  338. --*/
  339. {
  340. PLIST_ENTRY Link;
  341. USHORT PortBase;
  342. PNAT_PORT_BLOCK PortBlock;
  343. PNAT_PORT_RESERVATION PortReservation =
  344. (PNAT_PORT_RESERVATION)ReservationHandle;
  345. EnterCriticalSection(&PortReservation->Lock);
  346. //
  347. // Convert the caller's port-base into host-order,
  348. // and search the sorted list of port-blocks for the entry
  349. // from which the acquisition was made.
  350. //
  351. PortBase = RtlUshortByteSwap(ReservedPortBase);
  352. for (Link = PortReservation->PortBlockList.Flink;
  353. Link != &PortReservation->PortBlockList; Link = Link->Flink) {
  354. PortBlock = CONTAINING_RECORD(Link, NAT_PORT_BLOCK, Link);
  355. if (PortBase < PortBlock->StartHandle) {
  356. break;
  357. } else if (PortBase <
  358. (PortBlock->StartHandle + PortReservation->BlockSize)) {
  359. //
  360. // This should be the block from which the caller's port-numbers
  361. // were acquired. For good measure, check that the end of the
  362. // callers range also falls within this block.
  363. //
  364. if ((PortBase + PortCount - 1) >=
  365. (USHORT)(PortBlock->StartHandle + PortReservation->BlockSize)) {
  366. //
  367. // The caller has probably supplied an incorrect length,
  368. // or is releasing an allocation twice, or something.
  369. //
  370. LeaveCriticalSection(&PortReservation->Lock);
  371. return ERROR_INVALID_PARAMETER;
  372. } else {
  373. //
  374. // This is the caller's range. Clear the bits corresponding
  375. // to the caller's acquisition, and then see if there are
  376. // any bits left in the bitmap. If not, and if there are
  377. // other port-blocks, delete this port-block altogether.
  378. //
  379. RtlClearBits(
  380. &PortBlock->Bitmap,
  381. PortBase - PortBlock->StartHandle,
  382. PortCount
  383. );
  384. if (RtlFindSetBits(&PortBlock->Bitmap, 1, 0) == (ULONG)-1 &&
  385. (PortBlock->Link.Flink != &PortReservation->PortBlockList ||
  386. PortBlock->Link.Blink != &PortReservation->PortBlockList)
  387. ) {
  388. NatpDeletePortBlock(PortReservation, PortBlock);
  389. }
  390. LeaveCriticalSection(&PortReservation->Lock);
  391. return NO_ERROR;
  392. }
  393. } else {
  394. continue;
  395. }
  396. }
  397. LeaveCriticalSection(&PortReservation->Lock);
  398. //
  399. // We could not find the port-block from which the caller
  400. // allegedly acquired this range of port-numbers.
  401. //
  402. return ERROR_CAN_NOT_COMPLETE;
  403. } // NatReleasePortReservation
  404. VOID
  405. NatShutdownPortReservation(
  406. HANDLE ReservationHandle
  407. )
  408. /*++
  409. Routine Description:
  410. This routine is called to clean up a handle to the port-reservation module.
  411. It releases all reservations acquired, and closes the handle to the TCP/IP
  412. driver.
  413. Arguments:
  414. ReservationHandle - the handle to be cleaned up
  415. Return Value:
  416. none.
  417. --*/
  418. {
  419. PNAT_PORT_BLOCK PortBlock;
  420. PNAT_PORT_RESERVATION PortReservation =
  421. (PNAT_PORT_RESERVATION)ReservationHandle;
  422. while (!IsListEmpty(&PortReservation->PortBlockList)) {
  423. PortBlock =
  424. CONTAINING_RECORD(
  425. PortReservation->PortBlockList.Flink, NAT_PORT_BLOCK, Link
  426. );
  427. NatpDeletePortBlock(PortReservation, PortBlock);
  428. }
  429. NtClose(PortReservation->TcpipHandle);
  430. DeleteCriticalSection(&PortReservation->Lock);
  431. FREE(PortReservation);
  432. }