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.

608 lines
15 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. portmgmt.cpp
  5. Abstract:
  6. Functions for allocating and freeing ports from the Port pool
  7. PortPoolAllocRTPPort()
  8. PortPoolFreeRTPPort()
  9. Environment:
  10. User Mode - Win32
  11. --*/
  12. ///////////////////////////////////////////////////////////////////////////////
  13. // //
  14. // Functions dealing with the TCP device to reserve/unreserve port ranges. //
  15. // //
  16. ///////////////////////////////////////////////////////////////////////////////
  17. ///////////////////////////////////////////////////////////////////////////////
  18. // //
  19. // Include files //
  20. // //
  21. ///////////////////////////////////////////////////////////////////////////////
  22. #include "stdafx.h"
  23. #define NUM_DWORD_BITS (sizeof(DWORD)*8)
  24. ///////////////////////////////////////////////////////////////////////////////
  25. // //
  26. // Global Variables //
  27. // //
  28. ///////////////////////////////////////////////////////////////////////////////
  29. #define NUM_PORTS_PER_RANGE 100
  30. struct PORT_RANGE
  31. {
  32. LIST_ENTRY ListEntry;
  33. // This is the actual lower port. In case, the range allocated
  34. // by TCP starts with an odd port, we ignore the first port in
  35. // which case (low == AllocatedLow + 1). But when we free the
  36. // port range we should pass in AllocatedLow and not low.
  37. WORD AllocatedLow;
  38. WORD low;
  39. // high is the last port we can use and not the allocated high.
  40. // In some cases high will be one less than the actual allocated high.
  41. WORD high;
  42. //Each bit in this bitmap indicates the status of 2 consecutive ports
  43. DWORD *AllocList;
  44. DWORD dwAllocListSize;
  45. };
  46. class PORT_POOL :
  47. public SIMPLE_CRITICAL_SECTION_BASE
  48. {
  49. private:
  50. HANDLE TcpDevice;
  51. LIST_ENTRY PortRangeList; // contains PORT_RANGE.ListEntry
  52. private:
  53. HRESULT OpenTcpDevice (void);
  54. HRESULT StartLocked (void);
  55. void FreeAll (void);
  56. HRESULT CreatePortRange (
  57. OUT PORT_RANGE ** ReturnPortRange);
  58. HRESULT ReservePortRange (
  59. IN ULONG RangeLength,
  60. OUT WORD * ReturnStartPort);
  61. HRESULT UnReservePortRange (
  62. IN WORD StartPort);
  63. public:
  64. PORT_POOL (void);
  65. ~PORT_POOL (void);
  66. HRESULT Start (void);
  67. void Stop (void);
  68. HRESULT AllocPort (
  69. OUT WORD * ReturnPort);
  70. void FreePort (
  71. IN WORD Port);
  72. };
  73. // global data -------------------------------------------------------------------------
  74. static PORT_POOL PortPool;
  75. // extern code -----------------------------------------------------------------------
  76. HRESULT PortPoolStart (void)
  77. {
  78. return PortPool.Start();
  79. }
  80. void PortPoolStop (void)
  81. {
  82. PortPool.Stop();
  83. }
  84. HRESULT PortPoolAllocRTPPort (
  85. OUT WORD * ReturnPort)
  86. {
  87. return PortPool.AllocPort (ReturnPort);
  88. }
  89. HRESULT PortPoolFreeRTPPort (
  90. IN WORD Port)
  91. {
  92. PortPool.FreePort (Port);
  93. return S_OK;
  94. }
  95. HRESULT PORT_POOL::ReservePortRange (
  96. IN ULONG RangeLength,
  97. OUT WORD * ReturnStartPort)
  98. {
  99. TCP_BLOCKPORTS_REQUEST PortRequest;
  100. DWORD BytesTransferred;
  101. ULONG StartPort;
  102. AssertLocked();
  103. *ReturnStartPort = 0;
  104. if (!TcpDevice) {
  105. Debug (_T("H323: Cannot allocate port range, TCP device could not be opened.\n"));
  106. return E_UNEXPECTED;
  107. }
  108. assert (TcpDevice != INVALID_HANDLE_VALUE);
  109. PortRequest.ReservePorts = TRUE;
  110. PortRequest.NumberofPorts = RangeLength;
  111. if (!DeviceIoControl (TcpDevice, IOCTL_TCP_BLOCK_PORTS,
  112. &PortRequest, sizeof PortRequest,
  113. &StartPort, sizeof StartPort,
  114. &BytesTransferred, NULL)) {
  115. DebugLastError (_T("H323: Failed to allocate TCP port range.\n"));
  116. return GetLastError();
  117. }
  118. DebugF (_T("H323: Reserved port range: [%04X - %04X)\n"),
  119. StartPort, StartPort + PortRequest.NumberofPorts);
  120. *ReturnStartPort = (WORD) StartPort;
  121. return S_OK;
  122. }
  123. HRESULT PORT_POOL::UnReservePortRange (
  124. IN WORD StartPort)
  125. {
  126. TCP_BLOCKPORTS_REQUEST PortRequest;
  127. DWORD BytesTransferred;
  128. DWORD Status;
  129. AssertLocked();
  130. if (!TcpDevice) {
  131. Debug (_T("H323: Cannot free TCP port range, TCP device is not open.\n"));
  132. return E_UNEXPECTED;
  133. }
  134. assert (TcpDevice != INVALID_HANDLE_VALUE);
  135. PortRequest.ReservePorts = FALSE;
  136. PortRequest.StartHandle = (ULONG) StartPort;
  137. if (!DeviceIoControl(TcpDevice, IOCTL_TCP_BLOCK_PORTS,
  138. &PortRequest, sizeof PortRequest,
  139. &Status, sizeof Status,
  140. &BytesTransferred, NULL)) {
  141. DebugLastError (_T("H323: Failed to free TCP port range.\n"));
  142. return GetLastError();
  143. }
  144. return S_OK;
  145. }
  146. ///////////////////////////////////////////////////////////////////////////////
  147. // //
  148. // Port Pool Functions. //
  149. // //
  150. ///////////////////////////////////////////////////////////////////////////////
  151. // PORT_POOL -----------------------------------------------------------------------
  152. PORT_POOL::PORT_POOL (void)
  153. {
  154. TcpDevice = NULL;
  155. InitializeListHead (&PortRangeList);
  156. }
  157. PORT_POOL::~PORT_POOL (void)
  158. {
  159. assert (!TcpDevice);
  160. assert (IsListEmpty (&PortRangeList));
  161. }
  162. HRESULT PORT_POOL::Start (void)
  163. {
  164. HRESULT Result;
  165. Lock();
  166. Result = OpenTcpDevice();
  167. Unlock();
  168. return Result;
  169. }
  170. HRESULT PORT_POOL::OpenTcpDevice (void)
  171. {
  172. UNICODE_STRING DeviceName;
  173. IO_STATUS_BLOCK IoStatusBlock;
  174. OBJECT_ATTRIBUTES ObjectAttributes;
  175. NTSTATUS Status;
  176. if (TcpDevice)
  177. return S_OK;
  178. RtlInitUnicodeString (&DeviceName, (PCWSTR) DD_TCP_DEVICE_NAME);
  179. InitializeObjectAttributes (&ObjectAttributes, &DeviceName,
  180. OBJ_CASE_INSENSITIVE, NULL, NULL);
  181. Status = NtCreateFile (
  182. &TcpDevice,
  183. SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA ,
  184. &ObjectAttributes,
  185. &IoStatusBlock,
  186. NULL,
  187. FILE_ATTRIBUTE_NORMAL,
  188. FILE_SHARE_READ | FILE_SHARE_WRITE,
  189. FILE_OPEN_IF, 0, NULL, 0);
  190. if (Status != STATUS_SUCCESS) {
  191. TcpDevice = NULL;
  192. DebugError (Status, _T("H323: Failed to open TCP device.\n"));
  193. return (HRESULT) Status;
  194. }
  195. return S_OK;
  196. }
  197. void PORT_POOL::Stop (void)
  198. {
  199. Lock();
  200. FreeAll();
  201. if (TcpDevice) {
  202. assert (TcpDevice != INVALID_HANDLE_VALUE);
  203. CloseHandle (TcpDevice);
  204. TcpDevice = NULL;
  205. }
  206. Unlock();
  207. }
  208. void PORT_POOL::FreeAll (void)
  209. {
  210. LIST_ENTRY * ListEntry;
  211. PORT_RANGE * PortRange;
  212. while (!IsListEmpty (&PortRangeList)) {
  213. ListEntry = RemoveHeadList (&PortRangeList);
  214. PortRange = CONTAINING_RECORD (ListEntry, PORT_RANGE, ListEntry);
  215. // Free the port range PortRange->AllocatedLow
  216. UnReservePortRange (PortRange -> AllocatedLow);
  217. EM_FREE (PortRange);
  218. }
  219. }
  220. /*++
  221. Routine Description:
  222. This function allocates a pair of RTP/RTCP ports from the
  223. port pool.
  224. Arguments:
  225. rRTPport - This is an OUT parameter. If the function succeeds
  226. rRTPport will contain the RTP port (which is even).
  227. rRTPport+1 should be used as the RTCP port.
  228. Return Values:
  229. This function returns S_OK on success and E_FAIL if it
  230. fails to allocate a port range.
  231. --*/
  232. HRESULT PORT_POOL::AllocPort (
  233. OUT WORD * ReturnPort)
  234. {
  235. DWORD i, j;
  236. DWORD bitmap = 0x80000000;
  237. LIST_ENTRY * ListEntry;
  238. PORT_RANGE * PortRange;
  239. WORD Port;
  240. HRESULT Result;
  241. Lock();
  242. for (ListEntry = PortRangeList.Flink; ListEntry != &PortRangeList; ListEntry = ListEntry -> Flink) {
  243. PortRange = CONTAINING_RECORD (ListEntry, PORT_RANGE, ListEntry);
  244. for (i = 0; i < PortRange->dwAllocListSize; i++) {
  245. // traverse through AllocList of this portRange
  246. if ((PortRange->AllocList[i] & 0xffffffff) != 0xffffffff) {
  247. // at least one entry is free
  248. bitmap = 0x80000000;
  249. for (j = 0; j < NUM_DWORD_BITS; j++) {
  250. // traverse through each bit of the DWORD
  251. if ((PortRange->AllocList[i] & bitmap) == 0)
  252. {
  253. // found a free pair of ports
  254. Port = (WORD) (PortRange -> low + (i*NUM_DWORD_BITS*2) + (j*2));
  255. if (Port > PortRange -> high) {
  256. // This check is needed because the last DWORD
  257. // in the AllocList may contain bits which are
  258. // actually not included in the AllocList.
  259. goto noports;
  260. }
  261. // set the bit to show the pair of ports is allocated
  262. PortRange -> AllocList[i] |= bitmap;
  263. // Leave the global critical section for the Port pool
  264. Unlock();
  265. DebugF (_T("H323: Allocated port pair (%04X, %04X).\n"), Port, Port + 1);
  266. *ReturnPort = Port;
  267. return S_OK;
  268. }
  269. bitmap = bitmap >> 1;
  270. }
  271. }
  272. }
  273. }
  274. noports:
  275. // CODEWORK: Once we get the new ioctl() for dynamically reserving
  276. // port ranges, we need to allocate a new port range here. If the
  277. // ioctl() fails we need to return E_FAIL or another error which
  278. // says we have run out of ports.
  279. // Allocate a new port range
  280. Result = CreatePortRange (&PortRange);
  281. if (PortRange) {
  282. InsertHeadList (&PortRangeList, &PortRange -> ListEntry);
  283. // allocate the first port in the range and
  284. Port = PortRange -> low;
  285. PortRange->AllocList[0] |= 0x80000000;
  286. DebugF (_T("H323: Allocated port pair (%04X, %04X).\n"),
  287. Port, Port + 1);
  288. *ReturnPort = Port;
  289. Result = S_OK;
  290. }
  291. else {
  292. Debug (_T("H323: Failed to allocate port range.\n"));
  293. *ReturnPort = 0;
  294. Result = E_FAIL;
  295. }
  296. Unlock();
  297. return Result;
  298. }
  299. /*++
  300. Routine Description:
  301. This function frees a pair of RTP/RTCP ports.
  302. The data structure is changed to show that the pair of ports
  303. is now available.
  304. CODEWORK: If an entire port range becomes free, do we release
  305. the port range to the operating system ? We probably need a
  306. heuristic to do this because allocating a port range again
  307. could be an expensive operation.
  308. Arguments:
  309. wRTPport - This gives the RTP port to be freed.
  310. (RTCP port is RTPport+1 which is implicitly freed because
  311. we use one bit store the status of both these ports.)
  312. Return Values:
  313. Returns S_OK on success or E_FAIL if the port is not found in
  314. the port pool list.
  315. --*/
  316. void PORT_POOL::FreePort (
  317. IN WORD Port)
  318. {
  319. HRESULT Result;
  320. // assert RTP port is even
  321. _ASSERTE ((Port & 1) == 0);
  322. DWORD Index = 0;
  323. DWORD Bitmap = 0x80000000;
  324. LIST_ENTRY * ListEntry;
  325. PORT_RANGE * PortRange;
  326. Lock();
  327. // find the port range that this port belongs to
  328. // simple linear scan -- suboptimal
  329. Result = E_FAIL;
  330. for (ListEntry = PortRangeList.Flink; ListEntry != &PortRangeList; ListEntry = ListEntry -> Flink) {
  331. PortRange = CONTAINING_RECORD (ListEntry, PORT_RANGE, ListEntry);
  332. if (PortRange -> low <= Port && PortRange -> high >= Port) {
  333. Result = S_OK;
  334. break;
  335. }
  336. }
  337. if (Result == S_OK) {
  338. Index = (Port - PortRange -> low) / (NUM_DWORD_BITS * 2);
  339. // assert index is less than the size of the array
  340. _ASSERTE (Index < PortRange -> dwAllocListSize);
  341. // CODEWORK: make sure that the bit is set i.e. the port has
  342. // been previously allocated. Otherwise return an error and print
  343. // a warning.
  344. // zero the bit to show the pair of ports is now free
  345. PortRange -> AllocList [Index] &=
  346. ~(Bitmap >> (((Port - PortRange -> low) / 2) % NUM_DWORD_BITS));
  347. DebugF (_T("H323: Deallocated port pair (%04X, %04X).\n"), Port, Port + 1);
  348. }
  349. else {
  350. DebugF (_T("H323: warning, attempted to free port pair (%04X, %04X), but it did not belong to any port range.\n"),
  351. Port, Port + 1);
  352. }
  353. Unlock();
  354. }
  355. HRESULT PORT_POOL::CreatePortRange (
  356. OUT PORT_RANGE ** ReturnPortRange)
  357. {
  358. // CODEWORK: Once we get the new ioctl() for dynamically reserving
  359. // port ranges, we need to allocate a new port range here. If the
  360. // ioctl() fails we need to return E_FAIL or another error which
  361. // says we have run out of ports.
  362. // assert low is even and high is odd
  363. // _ASSERTE((low % 2) == 0);
  364. // _ASSERTE((high % 2) == 1);
  365. HRESULT Result;
  366. WORD AllocatedLowerPort;
  367. WORD LowerPort;
  368. DWORD NumPortsInRange;
  369. PORT_RANGE * PortRange;
  370. DWORD dwAllocListSize;
  371. assert (ReturnPortRange);
  372. *ReturnPortRange = NULL;
  373. Result = ReservePortRange (NUM_PORTS_PER_RANGE, &AllocatedLowerPort);
  374. if (FAILED (Result))
  375. return Result;
  376. // If the allocated lower port is odd we do not use the lower port
  377. // and the range we use starts with the next higher port.
  378. if ((AllocatedLowerPort & 1) == 1) {
  379. // the allocated region is ODD
  380. // don't use the first entry
  381. NumPortsInRange = NUM_PORTS_PER_RANGE - 1 - ((NUM_PORTS_PER_RANGE) & 1);
  382. LowerPort = AllocatedLowerPort + 1;
  383. }
  384. else {
  385. // the allocated region is EVEN
  386. // don't use the last entry
  387. NumPortsInRange = NUM_PORTS_PER_RANGE;
  388. LowerPort = AllocatedLowerPort;
  389. }
  390. // If NumPortsInRange is odd, we can not use the last port
  391. if ((NumPortsInRange & 1) == 1)
  392. {
  393. NumPortsInRange--;
  394. }
  395. // Each bit gives the status (free/allocated) of two consecutive
  396. // ports. So, each DWORD can store the status of NUM_DWORD_BITS*2
  397. // ports. We add (NUM_DWORD_BITS*2 - 1) to round up the number of
  398. // DWORDS required.
  399. dwAllocListSize = (NumPortsInRange + NUM_DWORD_BITS*2 - 1)
  400. / (NUM_DWORD_BITS * 2);
  401. // allocate space for the AllocList also
  402. // Since we do not anticipate too many port ranges being allocated,
  403. // we do not require a separate heap for these structures.
  404. PortRange = (PORT_RANGE *) EM_MALLOC (
  405. sizeof (PORT_RANGE) + dwAllocListSize * sizeof (DWORD));
  406. if (PortRange == NULL) {
  407. Debug (_T("H323: Allocation failure, cannot allocate PORT_RANGE and associated bit map\n"));
  408. UnReservePortRange (AllocatedLowerPort);
  409. return E_OUTOFMEMORY;
  410. }
  411. _ASSERTE((LowerPort + NumPortsInRange - 1) <= 0xFFFF);
  412. PortRange -> AllocatedLow = AllocatedLowerPort;
  413. PortRange -> low = LowerPort;
  414. PortRange -> high = (WORD) (LowerPort + NumPortsInRange - 1);
  415. PortRange -> dwAllocListSize = dwAllocListSize;
  416. PortRange -> AllocList = (DWORD *) (PortRange + 1);
  417. DebugF (_T("H323: Allocated port block: [%04X - %04X].\n"),
  418. PortRange -> low,
  419. PortRange -> high,
  420. PortRange -> dwAllocListSize);
  421. // Initialize the AllocList to show all the ports are free
  422. ZeroMemory (PortRange -> AllocList, (PortRange -> dwAllocListSize) * sizeof (DWORD));
  423. *ReturnPortRange = PortRange;
  424. return S_OK;
  425. }