Source code of Windows XP (NT5)
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.

705 lines
17 KiB

  1. /*++
  2. Module Name:
  3. director.c
  4. Abstract:
  5. This module implements a driver which demonstrates the use of
  6. the IP NAT's support for directing incoming sessions.
  7. The driver reads a protocol, port and server-list from its 'Parameters'
  8. subkey, and directs all sessions on the specified protocol and port
  9. to the servers in the list, in a round-robin fashion.
  10. The expected registry configuration is as follows:
  11. IPNATDIR\Parameters
  12. ServerProtocol REG_DWORD 0x6 (TCP) or 0x11 (UDP)
  13. ServerPort REG_DWORD 1-65535
  14. ServerList REG_MULTI_SZ List of dotted-decimal IP addresses.
  15. Author:
  16. Abolade Gbadegesin (aboladeg) 16-Feb-1998
  17. Revision History:
  18. --*/
  19. #include <ntddk.h>
  20. #include <ipnat.h>
  21. #define NT_DEVICE_NAME L"\\Device\\IPNATDIR"
  22. #define DOS_DEVICE_NAME L"\\DosDevices\\IPNATDIR"
  23. #define ULONG_CHAR_LIST(a) \
  24. ((a) & 0x000000FF), (((a) & 0x0000FF00) >> 8), \
  25. (((a) & 0x00FF0000) >> 16), (((a) & 0xFF000000) >> 24)
  26. #define NTOHS(p) ((((p) & 0xFF00) >> 8) | (((UCHAR)(p) << 8)))
  27. typedef struct _SERVER_ENTRY {
  28. LIST_ENTRY Link;
  29. ULONG Address;
  30. ULONG SessionCount;
  31. } SERVER_ENTRY, *PSERVER_ENTRY;
  32. //
  33. // GLOBAL DATA
  34. //
  35. IP_NAT_REGISTER_DIRECTOR DirRegisterDirector;
  36. LIST_ENTRY DirServerList;
  37. KSPIN_LOCK DirServerLock;
  38. USHORT DirServerPort = NTOHS(1000);
  39. UCHAR DirServerProtocol = NAT_PROTOCOL_TCP;
  40. //
  41. // FORWARD DECLARATIONS
  42. //
  43. NTSTATUS
  44. DirCleanup(
  45. IN PDEVICE_OBJECT DeviceObject,
  46. IN PIRP Irp
  47. );
  48. NTSTATUS
  49. DirClose(
  50. IN PDEVICE_OBJECT DeviceObject,
  51. IN PIRP Irp
  52. );
  53. NTSTATUS
  54. DirOpen(
  55. IN PDEVICE_OBJECT DeviceObject,
  56. IN PIRP Irp
  57. );
  58. ULONG
  59. InetAddr(
  60. PWCHAR String
  61. );
  62. NTSTATUS
  63. DirRegister(
  64. VOID
  65. );
  66. VOID
  67. DirUnload(
  68. IN PDRIVER_OBJECT DriverObject
  69. );
  70. NTSTATUS
  71. DriverEntry(
  72. IN PDRIVER_OBJECT DriverObject,
  73. IN PUNICODE_STRING RegistryPath
  74. )
  75. /*++
  76. Routine Description:
  77. This routine implements the standard driver-entry for an NT driver.
  78. It is responsible for reading configuration from the registry,
  79. and registering our entrypoints with the NAT driver.
  80. Arguments:
  81. DriverObject - object to be initialized with NT driver entrypoints
  82. RegistryPath - contains path to this driver's registry key
  83. Return Value:
  84. NTSTATUS - indicates success/failure.
  85. --*/
  86. {
  87. PDEVICE_OBJECT DeviceObject = NULL;
  88. UNICODE_STRING NtDeviceName;
  89. OBJECT_ATTRIBUTES ObjectAttributes;
  90. HANDLE ServiceKey;
  91. NTSTATUS status;
  92. UNICODE_STRING Win32DeviceName;
  93. KdPrint(("DirDriverEntry\n"));
  94. InitializeListHead(&DirServerList);
  95. KeInitializeSpinLock(&DirServerLock);
  96. //
  97. // Create the device object
  98. //
  99. RtlInitUnicodeString(&NtDeviceName, NT_DEVICE_NAME);
  100. status =
  101. IoCreateDevice(
  102. DriverObject,
  103. 0,
  104. &NtDeviceName,
  105. FILE_DEVICE_UNKNOWN,
  106. 0,
  107. TRUE,
  108. &DeviceObject
  109. );
  110. if (!NT_SUCCESS(status)) {
  111. KdPrint(("DirDriverEntry: IoCreateDevice=%08x\n", status));
  112. return status;
  113. }
  114. //
  115. // Create dispatch points for create/open, close, unload.
  116. //
  117. DriverObject->MajorFunction[IRP_MJ_CREATE] = DirOpen;
  118. DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DirCleanup;
  119. DriverObject->MajorFunction[IRP_MJ_CLOSE] = DirClose;
  120. DriverObject->DriverUnload = DirUnload;
  121. //
  122. // Create counted string version of our Win32 device name.
  123. //
  124. RtlInitUnicodeString(&Win32DeviceName, DOS_DEVICE_NAME);
  125. //
  126. // Create a link from our device name to a name in the Win32 namespace.
  127. //
  128. status = IoCreateSymbolicLink(&Win32DeviceName, &NtDeviceName);
  129. if (!NT_SUCCESS(status)) {
  130. KdPrint(("DirDriverEntry: IoCreateSymbolLink=%08x\n", status));
  131. IoDeleteDevice(DriverObject->DeviceObject);
  132. return status;
  133. }
  134. //
  135. // Read registry configuration, if any.
  136. //
  137. InitializeObjectAttributes(
  138. &ObjectAttributes,
  139. RegistryPath,
  140. OBJ_CASE_INSENSITIVE,
  141. NULL,
  142. NULL
  143. );
  144. status = ZwOpenKey(&ServiceKey, KEY_READ, &ObjectAttributes);
  145. if (NT_SUCCESS(status)) {
  146. HANDLE Key;
  147. UNICODE_STRING UnicodeString;
  148. RtlInitUnicodeString(&UnicodeString, L"Parameters");
  149. InitializeObjectAttributes(
  150. &ObjectAttributes,
  151. &UnicodeString,
  152. OBJ_CASE_INSENSITIVE,
  153. ServiceKey,
  154. NULL
  155. );
  156. status = ZwOpenKey(&Key, KEY_READ, &ObjectAttributes);
  157. ZwClose(ServiceKey);
  158. if (NT_SUCCESS(status)) {
  159. UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 32];
  160. ULONG Length;
  161. PKEY_VALUE_PARTIAL_INFORMATION Value =
  162. (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
  163. //
  164. // Read the protocol name
  165. //
  166. RtlInitUnicodeString(&UnicodeString, L"ServerProtocol");
  167. status =
  168. ZwQueryValueKey(
  169. Key,
  170. &UnicodeString,
  171. KeyValuePartialInformation,
  172. (PKEY_VALUE_PARTIAL_INFORMATION)Buffer,
  173. sizeof(Buffer),
  174. &Length
  175. );
  176. if (NT_SUCCESS(status)) {
  177. if (_wcsicmp((PWCHAR)Value->Data, L"UDP") == 0) {
  178. DirServerPort = NAT_PROTOCOL_UDP;
  179. KdPrint(("DirDriverEntry: read protocol UDP\n"));
  180. } else {
  181. DirServerPort = NAT_PROTOCOL_TCP;
  182. KdPrint(("DirDriverEntry: read protocol TCP\n"));
  183. }
  184. }
  185. //
  186. // Read the destination port
  187. //
  188. RtlInitUnicodeString(&UnicodeString, L"ServerPort");
  189. status =
  190. ZwQueryValueKey(
  191. Key,
  192. &UnicodeString,
  193. KeyValuePartialInformation,
  194. (PKEY_VALUE_PARTIAL_INFORMATION)Buffer,
  195. sizeof(Buffer),
  196. &Length
  197. );
  198. if (NT_SUCCESS(status)) {
  199. DirServerPort = (USHORT)*(PULONG)Value->Data;
  200. KdPrint(("DirDriverEntry: read port %d\n", DirServerPort));
  201. DirServerPort = NTOHS(DirServerPort);
  202. }
  203. //
  204. // Read the list of servers
  205. //
  206. RtlInitUnicodeString(&UnicodeString, L"ServerList");
  207. status =
  208. ZwQueryValueKey(
  209. Key,
  210. &UnicodeString,
  211. KeyValuePartialInformation,
  212. (PKEY_VALUE_PARTIAL_INFORMATION)Buffer,
  213. sizeof(KEY_VALUE_PARTIAL_INFORMATION),
  214. &Length
  215. );
  216. if (status == STATUS_BUFFER_OVERFLOW) {
  217. Value = ExAllocatePool(PagedPool, Length);
  218. if (Value) {
  219. status =
  220. ZwQueryValueKey(
  221. Key,
  222. &UnicodeString,
  223. KeyValuePartialInformation,
  224. Value,
  225. Length,
  226. &Length
  227. );
  228. if (NT_SUCCESS(status)) {
  229. //
  230. // Parse the server-list
  231. //
  232. PWCHAR String;
  233. PSERVER_ENTRY Entry;
  234. for (String = (PWCHAR)Value->Data;
  235. String[0];
  236. String += wcslen(String) + 1
  237. ) {
  238. KdPrint(("DirDriverEntry: read %ls\n", String));
  239. Entry = ExAllocatePool(PagedPool, sizeof(*Entry));
  240. if (!Entry) { continue; }
  241. Entry->Address = InetAddr(String);
  242. if (!Entry->Address) {
  243. ExFreePool(Entry);
  244. } else {
  245. Entry->SessionCount = 0;
  246. InsertTailList(&DirServerList, &Entry->Link);
  247. }
  248. }
  249. }
  250. ExFreePool(Value);
  251. }
  252. }
  253. }
  254. }
  255. //
  256. // Register with the NAT
  257. //
  258. return DirRegister();
  259. }
  260. NTSTATUS
  261. DirCleanup(
  262. IN PDEVICE_OBJECT DeviceObject,
  263. IN PIRP Irp
  264. )
  265. {
  266. KdPrint(("DirCleanup\n"));
  267. Irp->IoStatus.Status = STATUS_SUCCESS;
  268. Irp->IoStatus.Information = 0;
  269. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  270. return STATUS_SUCCESS;
  271. }
  272. NTSTATUS
  273. DirClose(
  274. IN PDEVICE_OBJECT DeviceObject,
  275. IN PIRP Irp
  276. )
  277. {
  278. KdPrint(("DirClose\n"));
  279. Irp->IoStatus.Status = STATUS_SUCCESS;
  280. Irp->IoStatus.Information = 0;
  281. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  282. return STATUS_SUCCESS;
  283. }
  284. NTSTATUS
  285. DirOpen(
  286. IN PDEVICE_OBJECT DeviceObject,
  287. IN PIRP Irp
  288. )
  289. {
  290. KdPrint(("DirOpen\n"));
  291. Irp->IoStatus.Status = STATUS_SUCCESS;
  292. Irp->IoStatus.Information = 0;
  293. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  294. return STATUS_SUCCESS;
  295. }
  296. NTSTATUS
  297. DirpCreateHandler(
  298. IN PVOID SessionHandle,
  299. IN PVOID DirectorContext,
  300. IN PVOID DirectorSessionContext
  301. )
  302. /*++
  303. Routine Description:
  304. This routine is invoked by the NAT when a session-mapping is successfully
  305. created after a query has been made.
  306. All we do here is increment the count of active sessions.
  307. Arguments:
  308. SessionHandle - opaquely identifies the new session
  309. DirectorContext - our director-object context
  310. DirectorSessionContext - our session-mapping context
  311. Return Value:
  312. STATUS_SUCCESS.
  313. --*/
  314. {
  315. PSERVER_ENTRY ServerEntry = (PSERVER_ENTRY)DirectorSessionContext;
  316. KdPrint((
  317. "DirCreateHandler: %d.%d.%d.%d [SessionCount=%d]\n",
  318. ULONG_CHAR_LIST(ServerEntry->Address),
  319. InterlockedIncrement(&ServerEntry->SessionCount)
  320. ));
  321. return STATUS_SUCCESS;
  322. }
  323. NTSTATUS
  324. DirpDeleteHandler(
  325. IN PVOID SessionHandle,
  326. IN PVOID DirectorContext,
  327. IN PVOID DirectorSessionContext,
  328. IN IP_NAT_DELETE_REASON DeleteReason
  329. )
  330. /*++
  331. Routine Description:
  332. This routine is invoked by the NAT in the following cases:
  333. (a) when a session-mapping is deleted (SessionHandle != NULL)
  334. (b) when a session-mapping cannot be created using the information
  335. supplied by previous call to 'QueryHandler' (SessionHandle == NULL).
  336. Arguments:
  337. SessionHandle - identifies the failed session
  338. DirectorContext - our director-object context
  339. DirectorSessionContext - our session-mapping context
  340. DeleteReason - the cause for session deletion
  341. Return Value:
  342. STATUS_SUCCESS
  343. --*/
  344. {
  345. PSERVER_ENTRY ServerEntry = (PSERVER_ENTRY)DirectorSessionContext;
  346. ULONG SessionCount =
  347. SessionHandle
  348. ? InterlockedDecrement(&ServerEntry->SessionCount)
  349. : ServerEntry->SessionCount;
  350. KdPrint((
  351. "DirDeleteHandler: %d.%d.%d.%d [SessionCount=%d]\n",
  352. ULONG_CHAR_LIST(ServerEntry->Address),
  353. SessionCount
  354. ));
  355. return STATUS_SUCCESS;
  356. }
  357. NTSTATUS
  358. DirpQueryHandler(
  359. PIP_NAT_DIRECTOR_QUERY DirectorQuery
  360. )
  361. /*++
  362. Routine Description:
  363. This routine is invoked by the NAT to determine whether a change
  364. should be made to an incoming packet which matches the protocol
  365. and port for which we are registered as a director.
  366. On input, 'DirectorQuery' contains the packets source and destination
  367. endpoints as well as flags which provide further information.
  368. On output, 'DirectorQuery' may be filled with replacement information
  369. and flags may be modified to control the creation of a mapping
  370. for the packet's session.
  371. Arguments:
  372. DirectorQuery - describes the session on input,
  373. filled with information for a mapping on output.
  374. Return Value:
  375. NTSTATUS - indicates whether a mapping should be created.
  376. --*/
  377. {
  378. PSERVER_ENTRY Entry;
  379. KdPrint((
  380. "DirQueryHandler:protocol=%d,%d.%d.%d.%d/%d-%d.%d.%d.%d/%d\n",
  381. DirectorQuery->Protocol,
  382. ULONG_CHAR_LIST(DirectorQuery->DestinationAddress),
  383. NTOHS(DirectorQuery->DestinationPort),
  384. ULONG_CHAR_LIST(DirectorQuery->SourceAddress),
  385. NTOHS(DirectorQuery->SourcePort)
  386. ));
  387. KeAcquireSpinLockAtDpcLevel(&DirServerLock);
  388. if (IsListEmpty(&DirServerList)) {
  389. KeReleaseSpinLockFromDpcLevel(&DirServerLock);
  390. return STATUS_UNSUCCESSFUL;
  391. }
  392. //
  393. // Direct the session to the first server-entry on the list
  394. //
  395. Entry = CONTAINING_RECORD(DirServerList.Flink, SERVER_ENTRY, Link);
  396. DirectorQuery->DirectorSessionContext = Entry;
  397. DirectorQuery->NewDestinationAddress = Entry->Address;
  398. DirectorQuery->NewDestinationPort = DirectorQuery->DestinationPort;
  399. DirectorQuery->NewSourceAddress = DirectorQuery->SourceAddress;
  400. DirectorQuery->NewSourcePort = DirectorQuery->SourcePort;
  401. //
  402. // Move the server-entry to the end of the list
  403. //
  404. RemoveEntryList(&Entry->Link);
  405. InsertTailList(&DirServerList, &Entry->Link);
  406. KeReleaseSpinLockFromDpcLevel(&DirServerLock);
  407. return STATUS_SUCCESS;
  408. }
  409. VOID
  410. DirpUnloadHandler(
  411. IN PVOID DirectorContext
  412. )
  413. /*++
  414. Routine Description:
  415. This routine is invoked by the NAT when the NAT driver is being unloaded.
  416. Arguments:
  417. DirectorContext - the context for this driver (unused)
  418. Return Value:
  419. none.
  420. --*/
  421. {
  422. KdPrint(("DirpUnloadHandler\n"));
  423. return;
  424. }
  425. NTSTATUS
  426. DirRegister(
  427. VOID
  428. )
  429. /*++
  430. Routine Description:
  431. This routine is invoked to register this driver with the NAT
  432. as a director for the configured protocol and port.
  433. Arguments:
  434. none.
  435. Return Status:
  436. NTSTATUS - indicates success/failure
  437. --*/
  438. {
  439. PDEVICE_OBJECT DeviceObject;
  440. UNICODE_STRING DeviceString;
  441. KEVENT Event;
  442. PFILE_OBJECT FileObject;
  443. IO_STATUS_BLOCK IoStatus;
  444. PIRP Irp;
  445. NTSTATUS status;
  446. KdPrint(("DirRegisterDirector\n"));
  447. //
  448. // Initialize the registration information
  449. //
  450. RtlZeroMemory(&DirRegisterDirector, sizeof(DirRegisterDirector));
  451. DirRegisterDirector.Version = IP_NAT_VERSION;
  452. DirRegisterDirector.Protocol = NAT_PROTOCOL_TCP;
  453. DirRegisterDirector.Port = DirServerPort;
  454. DirRegisterDirector.CreateHandler = DirpCreateHandler;
  455. DirRegisterDirector.DeleteHandler = DirpDeleteHandler;
  456. DirRegisterDirector.QueryHandler = DirpQueryHandler;
  457. //
  458. // Retrieve a pointer to the NAT device object
  459. //
  460. RtlInitUnicodeString(&DeviceString, DD_IP_NAT_DEVICE_NAME);
  461. status =
  462. IoGetDeviceObjectPointer(
  463. &DeviceString,
  464. SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
  465. &FileObject,
  466. &DeviceObject
  467. );
  468. if (!NT_SUCCESS(status)) { return status; }
  469. //
  470. // Create an IRP and use it to register with the NAT
  471. //
  472. ObReferenceObject(DeviceObject);
  473. KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
  474. Irp =
  475. IoBuildDeviceIoControlRequest(
  476. IOCTL_IP_NAT_REGISTER_DIRECTOR,
  477. DeviceObject,
  478. (PVOID)&DirRegisterDirector,
  479. sizeof(DirRegisterDirector),
  480. (PVOID)&DirRegisterDirector,
  481. sizeof(DirRegisterDirector),
  482. FALSE,
  483. &Event,
  484. &IoStatus
  485. );
  486. if (!Irp) {
  487. status = STATUS_UNSUCCESSFUL;
  488. } else {
  489. status = IoCallDriver(DeviceObject, Irp);
  490. if (status == STATUS_PENDING) {
  491. KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
  492. status = IoStatus.Status;
  493. }
  494. }
  495. ObDereferenceObject((PVOID)FileObject);
  496. ObDereferenceObject(DeviceObject);
  497. return status;
  498. } // DirRegisterDirector
  499. VOID
  500. DirUnload(
  501. IN PDRIVER_OBJECT DriverObject
  502. )
  503. /*++
  504. Routine Description:
  505. This routine is invoked by the I/O manager to unload this driver.
  506. Arguments:
  507. DriverObject - the object for this driver
  508. Return Value:
  509. none.
  510. --*/
  511. {
  512. PSERVER_ENTRY Entry;
  513. UNICODE_STRING Win32DeviceName;
  514. KdPrint(("DirUnload\n"));
  515. //
  516. // Deregister with the NAT, and cleanup our list of servers
  517. //
  518. DirRegisterDirector.Deregister(DirRegisterDirector.DirectorHandle);
  519. while (!IsListEmpty(&DirServerList)) {
  520. Entry = CONTAINING_RECORD(DirServerList.Flink, SERVER_ENTRY, Link);
  521. RemoveEntryList(&Entry->Link);
  522. ExFreePool(Entry);
  523. }
  524. //
  525. // Delete the link from our device name to a name in the Win32 namespace,
  526. // and delete our device object
  527. //
  528. RtlInitUnicodeString(&Win32DeviceName, DOS_DEVICE_NAME);
  529. IoDeleteSymbolicLink(&Win32DeviceName);
  530. IoDeleteDevice( DriverObject->DeviceObject );
  531. }
  532. ULONG
  533. InetAddr(
  534. PWCHAR String
  535. )
  536. {
  537. ULONG Digit;
  538. ULONG Fields[4] = {0, 0, 0, 0};
  539. ULONG i = 0;
  540. for (Digit = (*String - L'0');
  541. Digit <= 9 && i < 4;
  542. Digit = (*String - L'0')
  543. ) {
  544. Fields[i] = Fields[i] * 10 + Digit;
  545. if (*(++String) == L'.') { ++i; ++String; }
  546. }
  547. if (*String != L'\0' ||
  548. i != 3 ||
  549. Fields[0] > 255 ||
  550. Fields[1] > 255 ||
  551. Fields[2] > 255 ||
  552. Fields[3] > 255
  553. ) {
  554. return 0;
  555. }
  556. return
  557. (Fields[0]) |
  558. (Fields[1] << 8) |
  559. (Fields[2] << 16) |
  560. (Fields[3] << 24);
  561. }