1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS TDI test driver
4  * FILE:        tditest.c
5  * PURPOSE:     Testing TDI drivers
6  * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7  *              Vizzini (vizzini@plasmic.com)
8  * REVISIONS:
9  *   CSH 01/08-2000 Created
10  *   26-Nov-2003 Vizzini Updated to run properly on Win2ksp4
11  */
12 #include <tditest.h>
13 #include <pseh/pseh2.h>
14 
15 
16 #if DBG
17 
18 /* See debug.h for debug/trace constants */
19 ULONG DebugTraceLevel = -1;
20 
21 #endif /* DBG */
22 
23 
24 HANDLE TdiTransport             = 0;
25 PFILE_OBJECT TdiTransportObject = NULL;
26 ULONG LocalAddress;
27 BOOLEAN OpenError;
28 KEVENT StopEvent;
29 HANDLE SendThread;
30 HANDLE ReceiveThread;
31 
32 NTSTATUS TdiCall(
33     PIRP Irp,
34     PDEVICE_OBJECT DeviceObject,
35     PIO_STATUS_BLOCK IoStatusBlock,
36     BOOLEAN CanCancel)
37 /*
38  * FUNCTION: Calls a transport driver device
39  * ARGUMENTS:
40  *     Irp           = Pointer to I/O Request Packet
41  *     DeviceObject  = Pointer to device object to call
42  *     IoStatusBlock = Address of buffer with I/O status block
43  *     CanCancel     = TRUE if the IRP can be cancelled, FALSE if not
44  * RETURNS:
45  *     Status of operation
46  * NOTES
47  *     All requests are completed synchronously. A request may be cancelled
48  */
49 {
50 	KEVENT Event;
51 	PKEVENT Events[2];
52 	NTSTATUS Status;
53 	Events[0] = &StopEvent;
54 	Events[1] = &Event;
55 
56 	KeInitializeEvent(&Event, NotificationEvent, FALSE);
57 	Irp->UserEvent = &Event;
58 	Irp->UserIosb  = IoStatusBlock;
59 
60 	Status = IoCallDriver(DeviceObject, Irp);
61 
62 	if (Status == STATUS_PENDING)
63 		{
64 			if (CanCancel)
65 				{
66 					Status = KeWaitForMultipleObjects(2, (PVOID)Events, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
67 
68 					if (KeReadStateEvent(&StopEvent) != 0)
69 						{
70 							if (IoCancelIrp(Irp))
71 								{
72 									TDI_DbgPrint(MAX_TRACE, ("Cancelled IRP.\n"));
73 								}
74 							else
75 								{
76 									TDI_DbgPrint(MIN_TRACE, ("Could not cancel IRP.\n"));
77 								}
78 							return STATUS_CANCELLED;
79 						}
80 				}
81 			else
82 				Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
83 		}
84 
85 	return (Status == STATUS_SUCCESS)? IoStatusBlock->Status : STATUS_SUCCESS;
86 }
87 
88 
89 NTSTATUS TdiOpenDevice(
90     PWSTR Protocol,
91     ULONG EaLength,
92     PFILE_FULL_EA_INFORMATION EaInfo,
93     PHANDLE Handle,
94     PFILE_OBJECT *Object)
95 /*
96  * FUNCTION: Opens a device
97  * ARGUMENTS:
98  *     Protocol = Pointer to buffer with name of device
99  *     EaLength = Length of EA information
100  *     EaInfo   = Pointer to buffer with EA information
101  *     Handle   = Address of buffer to place device handle
102  *     Object   = Address of buffer to place device object
103  * RETURNS:
104  *     Status of operation
105  */
106 {
107 	OBJECT_ATTRIBUTES Attr;
108 	IO_STATUS_BLOCK Iosb;
109 	UNICODE_STRING Name;
110 	NTSTATUS Status;
111 
112 	RtlInitUnicodeString(&Name, Protocol);
113 	InitializeObjectAttributes(
114 		&Attr,                   /* Attribute buffer */
115 		&Name,                   /* Device name */
116 		OBJ_CASE_INSENSITIVE,    /* Attributes */
117 		NULL,                    /* Root directory */
118 		NULL);                   /* Security descriptor */
119 
120 	Status = ZwCreateFile(
121 		Handle,                               /* Return file handle */
122 		GENERIC_READ | GENERIC_WRITE,         /* Desired access */
123 		&Attr,                                /* Object attributes */
124 		&Iosb,                                /* IO status */
125 		0,                                    /* Initial allocation size */
126 		FILE_ATTRIBUTE_NORMAL,                /* File attributes */
127 		FILE_SHARE_READ | FILE_SHARE_WRITE,   /* Share access */
128 		FILE_OPEN_IF,                         /* Create disposition */
129 		0,                                    /* Create options */
130 		EaInfo,                               /* EA buffer */
131 		EaLength);                            /* EA length */
132 
133 	if (NT_SUCCESS(Status))
134 		{
135 			Status  = ObReferenceObjectByHandle(
136 				*Handle,                        /* Handle to open file */
137 				GENERIC_READ | GENERIC_WRITE,   /* Access mode */
138 				NULL,                           /* Object type */
139 				KernelMode,                     /* Access mode */
140 				(PVOID*)Object,                 /* Pointer to object */
141 				NULL);                          /* Handle information */
142 
143 			if (!NT_SUCCESS(Status))
144 				{
145 					TDI_DbgPrint(MIN_TRACE, ("ObReferenceObjectByHandle() failed with status (0x%X).\n", Status));
146 					ZwClose(*Handle);
147 				}
148 		}
149 	else
150 		{
151 			TDI_DbgPrint(MIN_TRACE, ("ZwCreateFile() failed with status (0x%X)\n", Status));
152 		}
153 
154     return Status;
155 }
156 
157 
158 NTSTATUS TdiCloseDevice(
159     HANDLE Handle,
160     PFILE_OBJECT FileObject)
161 {
162 	if (FileObject)
163 		ObDereferenceObject(FileObject);
164 
165 	if (Handle)
166 		ZwClose(Handle);
167 
168 	return STATUS_SUCCESS;
169 }
170 
171 
172 NTSTATUS TdiOpenTransport(
173     PWSTR Protocol,
174     USHORT Port,
175     PHANDLE Transport,
176     PFILE_OBJECT *TransportObject)
177 /*
178  * FUNCTION: Opens a transport driver
179  * ARGUMENTS:
180  *     Protocol        = Pointer to buffer with name of device
181  *     Port            = Port number to use
182  *     Transport       = Address of buffer to place transport device handle
183  *     TransportObject = Address of buffer to place transport object
184  * RETURNS:
185  *     Status of operation
186  */
187 {
188 	PFILE_FULL_EA_INFORMATION EaInfo;
189 	PTA_IP_ADDRESS Address;
190 	NTSTATUS Status;
191 	ULONG EaLength;
192 
193 	/* EaName must be 0-termed, even though TDI_TRANSPORT_ADDRESS_LENGTH does *not* include the 0 */
194 	EaLength = sizeof(FILE_FULL_EA_INFORMATION) + TDI_TRANSPORT_ADDRESS_LENGTH + sizeof(TA_IP_ADDRESS) + 1;
195 	EaInfo = (PFILE_FULL_EA_INFORMATION)ExAllocatePool(NonPagedPool, EaLength);
196 
197 	if (!EaInfo)
198 		{
199 			TDI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
200 			return STATUS_INSUFFICIENT_RESOURCES;
201 		}
202 
203 	RtlZeroMemory(EaInfo, EaLength);
204 
205 	EaInfo->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
206 
207 	/* don't copy the 0; we have already zeroed it */
208 	RtlCopyMemory(EaInfo->EaName, TdiTransportAddress, TDI_TRANSPORT_ADDRESS_LENGTH);
209 
210 	EaInfo->EaValueLength = sizeof(TA_IP_ADDRESS);
211 	Address = (PTA_IP_ADDRESS)(EaInfo->EaName + TDI_TRANSPORT_ADDRESS_LENGTH + 1); // 0-term
212 	Address->TAAddressCount                 = 1;
213 	Address->Address[0].AddressLength       = TDI_ADDRESS_LENGTH_IP;
214 	Address->Address[0].AddressType         = TDI_ADDRESS_TYPE_IP;
215 	Address->Address[0].Address[0].sin_port = WH2N(Port);
216 	Address->Address[0].Address[0].in_addr  = 0;
217 
218 	Status = TdiOpenDevice(Protocol, EaLength, EaInfo, Transport, TransportObject);
219 
220 	ExFreePool(EaInfo);
221 
222 	return Status;
223 }
224 
225 
226 NTSTATUS TdiQueryDeviceControl(
227     PFILE_OBJECT FileObject,
228     ULONG IoControlCode,
229     PVOID InputBuffer,
230     ULONG InputBufferLength,
231     PVOID OutputBuffer,
232     ULONG OutputBufferLength,
233     PULONG Return)
234 /*
235  * FUNCTION: Queries a device for information
236  * ARGUMENTS:
237  *     FileObject         = Pointer to device object
238  *     IoControlCode      = I/O control code
239  *     InputBuffer        = Pointer to buffer with input data
240  *     InputBufferLength  = Length of InputBuffer
241  *     OutputBuffer       = Address of buffer to place output data
242  *     OutputBufferLength = Length of OutputBuffer
243  * RETURNS:
244  *     Status of operation
245  */
246 {
247 	PDEVICE_OBJECT DeviceObject;
248 	PIO_STACK_LOCATION IoStack;
249 	IO_STATUS_BLOCK Iosb;
250 	NTSTATUS Status;
251 	PIRP Irp;
252 
253 	DeviceObject = IoGetRelatedDeviceObject(FileObject);
254 	Irp = IoBuildDeviceIoControlRequest(IoControlCode, DeviceObject, InputBuffer, InputBufferLength, OutputBuffer,
255 		OutputBufferLength, FALSE, NULL, NULL);
256 
257 	if (!Irp)
258 		{
259 			TDI_DbgPrint(MIN_TRACE, ("IoBuildDeviceIoControlRequest() failed.\n"));
260 			return STATUS_INSUFFICIENT_RESOURCES;
261 		}
262 
263 	IoStack               = IoGetNextIrpStackLocation(Irp);
264 	IoStack->DeviceObject = DeviceObject;
265 	IoStack->FileObject   = FileObject;
266 	Status = TdiCall(Irp, DeviceObject, &Iosb, FALSE);
267 
268 	if (Return)
269 		*Return = Iosb.Information;
270 
271 	return Status;
272 }
273 
274 
275 NTSTATUS TdiQueryInformationEx(
276     PFILE_OBJECT FileObject,
277     ULONG Entity,
278     ULONG Instance,
279     ULONG Class,
280     ULONG Type,
281     ULONG Id,
282     PVOID OutputBuffer,
283     PULONG OutputLength)
284 /*
285  * FUNCTION: Extended query for information
286  * ARGUMENTS:
287  *     FileObject   = Pointer to transport object
288  *     Entity       = Entity
289  *     Instance     = Instance
290  *     Class        = Entity class
291  *     Type         = Entity type
292  *     Id           = Entity id
293  *     OutputBuffer = Address of buffer to place data
294  *     OutputLength = Address of buffer with length of OutputBuffer (updated)
295  * RETURNS:
296  *     Status of operation
297  */
298 {
299  	TCP_REQUEST_QUERY_INFORMATION_EX QueryInfo;
300 
301 	RtlZeroMemory(&QueryInfo, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX));
302 	QueryInfo.ID.toi_entity.tei_entity   = Entity;
303 	QueryInfo.ID.toi_entity.tei_instance = Instance;
304 	QueryInfo.ID.toi_class = Class;
305 	QueryInfo.ID.toi_type  = Type;
306 	QueryInfo.ID.toi_id    = Id;
307 
308 	return TdiQueryDeviceControl(
309 		FileObject,                                /* Transport/connection object */
310 		IOCTL_TCP_QUERY_INFORMATION_EX,            /* Control code */
311 		&QueryInfo,                                /* Input buffer */
312 		sizeof(TCP_REQUEST_QUERY_INFORMATION_EX),  /* Input buffer length */
313 		OutputBuffer,                              /* Output buffer */
314 		*OutputLength,                             /* Output buffer length */
315 		OutputLength);                             /* Return information */
316 }
317 
318 
319 NTSTATUS TdiQueryAddress(
320     PFILE_OBJECT FileObject,
321     PULONG Address)
322 /*
323  * FUNCTION: Queries for a local IP address
324  * ARGUMENTS:
325  *     FileObject = Pointer to file object
326  *     Address    = Address of buffer to place local address
327  * RETURNS:
328  *     Status of operation
329  */
330 {
331 	ULONG i;
332 	TDIEntityID *Entities;
333 	ULONG EntityCount;
334 	ULONG EntityType;
335 	IPSNMP_INFO SnmpInfo;
336 	PIPADDR_ENTRY IpAddress;
337 	ULONG BufferSize;
338 	NTSTATUS Status = STATUS_SUCCESS;
339 
340 	TDI_DbgPrint(MAX_TRACE, ("Called\n"));
341 
342 	BufferSize = sizeof(TDIEntityID) * 20;
343 	Entities   = (TDIEntityID*)ExAllocatePool(NonPagedPool, BufferSize);
344 
345 	if (!Entities)
346 		{
347 			TDI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
348 			return STATUS_INSUFFICIENT_RESOURCES;
349 		}
350 
351 	/* Query device for supported entities */
352 	Status = TdiQueryInformationEx(
353 		FileObject,          /* File object */
354 		GENERIC_ENTITY,      /* Entity */
355 		TL_INSTANCE,         /* Instance */
356 		INFO_CLASS_GENERIC,  /* Entity class */
357 		INFO_TYPE_PROVIDER,  /* Entity type */
358 		ENTITY_LIST_ID,      /* Entity id */
359 		Entities,            /* Output buffer */
360 		&BufferSize);        /* Output buffer size */
361 
362 	if (!NT_SUCCESS(Status))
363 		{
364 			TDI_DbgPrint(MIN_TRACE, ("Unable to get list of supported entities (Status = 0x%X).\n", Status));
365 			ExFreePool(Entities);
366 			return Status;
367 		}
368 
369 	/* Locate an IP entity */
370 	EntityCount = BufferSize / sizeof(TDIEntityID);
371 
372 	TDI_DbgPrint(MAX_TRACE, ("EntityCount = %d\n", EntityCount));
373 
374 	for (i = 0; i < EntityCount; i++)
375 		{
376 			if (Entities[i].tei_entity == CL_NL_ENTITY)
377 				{
378 					/* Query device for entity type */
379 					BufferSize = sizeof(EntityType);
380 					Status = TdiQueryInformationEx(
381 						FileObject,                  /* File object */
382 						CL_NL_ENTITY,                /* Entity */
383 						Entities[i].tei_instance,    /* Instance */
384 						INFO_CLASS_GENERIC,          /* Entity class */
385 						INFO_TYPE_PROVIDER,          /* Entity type */
386 						ENTITY_TYPE_ID,              /* Entity id */
387 						&EntityType,                 /* Output buffer */
388 						&BufferSize);                /* Output buffer size */
389 
390 					if (!NT_SUCCESS(Status) || (EntityType != CL_NL_IP))
391 						{
392 							TDI_DbgPrint(MIN_TRACE, ("Unable to get entity of type IP (Status = 0x%X).\n", Status));
393 							break;
394 						}
395 
396 					/* Query device for SNMP information */
397 					BufferSize = sizeof(SnmpInfo);
398  					Status = TdiQueryInformationEx(
399 						FileObject,                  /* File object */
400 						CL_NL_ENTITY,                /* Entity */
401 						Entities[i].tei_instance,    /* Instance */
402 						INFO_CLASS_PROTOCOL,         /* Entity class */
403 						INFO_TYPE_PROVIDER,          /* Entity type */
404 						IP_MIB_STATS_ID,             /* Entity id */
405 						&SnmpInfo,                   /* Output buffer */
406 						&BufferSize);                /* Output buffer size */
407 
408 					if (!NT_SUCCESS(Status) || (SnmpInfo.NumAddr == 0))
409 						{
410 							TDI_DbgPrint(MIN_TRACE, ("Unable to get SNMP information or no IP addresses available (Status = 0x%X).\n", Status));
411 							break;
412 						}
413 
414 					/* Query device for all IP addresses */
415 					if (SnmpInfo.NumAddr != 0)
416 						{
417 							BufferSize = SnmpInfo.NumAddr * sizeof(IPADDR_ENTRY);
418 							IpAddress = (PIPADDR_ENTRY)ExAllocatePool(NonPagedPool, BufferSize);
419 							if (!IpAddress)
420 								{
421 									TDI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
422 									break;
423 								}
424 
425 						Status = TdiQueryInformationEx(
426 							FileObject,                  /* File object */
427 							CL_NL_ENTITY,                /* Entity */
428 							Entities[i].tei_instance,    /* Instance */
429 							INFO_CLASS_PROTOCOL,         /* Entity class */
430 							INFO_TYPE_PROVIDER,          /* Entity type */
431 							IP_MIB_ADDRTABLE_ENTRY_ID,   /* Entity id */
432 							IpAddress,                   /* Output buffer */
433 							&BufferSize);                /* Output buffer size */
434 
435 						if (!NT_SUCCESS(Status))
436 							{
437 								TDI_DbgPrint(MIN_TRACE, ("Unable to get IP address (Status = 0x%X).\n", Status));
438 								ExFreePool(IpAddress);
439 								break;
440 							}
441 
442 						if (SnmpInfo.NumAddr != 1)
443 							{
444 								/* Skip loopback address */
445 								*Address = DN2H(((PIPADDR_ENTRY)((PUCHAR)IpAddress + sizeof(IPADDR_ENTRY)))->Addr);
446 							}
447 						else
448 							{
449 								/* Select the first address returned */
450 								*Address = DN2H(IpAddress->Addr);
451 							}
452 								ExFreePool(IpAddress);
453 
454 						}
455 					else
456 						{
457 							Status = STATUS_UNSUCCESSFUL;
458 							break;
459 					}
460 			}
461 	}
462 
463 	ExFreePool(Entities);
464 
465 	TDI_DbgPrint(MAX_TRACE, ("Leaving\n"));
466 
467 	return Status;
468 }
469 
470 
471 NTSTATUS TdiSendDatagram(
472     PFILE_OBJECT TransportObject,
473     USHORT Port,
474     ULONG Address,
475     PVOID Buffer,
476     ULONG BufferSize)
477 /*
478  * FUNCTION: Sends a datagram
479  * ARGUMENTS:
480  *     TransportObject = Pointer to transport object
481  *     Port            = Remote port
482  *     Address         = Remote address
483  *     Buffer          = Pointer to buffer with data to send
484  *     BufferSize      = Length of Buffer
485  * RETURNS:
486  *     Status of operation
487  */
488 {
489 	PIRP Irp;
490 	PMDL Mdl;
491 	PDEVICE_OBJECT DeviceObject;
492 	PTDI_CONNECTION_INFORMATION ConnectInfo;
493 	PTA_IP_ADDRESS TA;
494 	PTDI_ADDRESS_IP IpAddress;
495 	IO_STATUS_BLOCK Iosb;
496 	NTSTATUS Status;
497 
498 	DeviceObject = IoGetRelatedDeviceObject(TransportObject);
499 	ConnectInfo  = (PTDI_CONNECTION_INFORMATION)
500 		ExAllocatePool(NonPagedPool,
501 		sizeof(TDI_CONNECTION_INFORMATION) +
502 		sizeof(TA_IP_ADDRESS));
503 
504 	if (!ConnectInfo)
505 		return STATUS_INSUFFICIENT_RESOURCES;
506 
507 	RtlZeroMemory(ConnectInfo, sizeof(TDI_CONNECTION_INFORMATION) + sizeof(TA_IP_ADDRESS));
508 
509 	ConnectInfo->RemoteAddressLength = sizeof(TA_IP_ADDRESS);
510 	ConnectInfo->RemoteAddress       = ((PUCHAR)ConnectInfo + sizeof(TDI_CONNECTION_INFORMATION));
511 
512 	TA = (PTA_IP_ADDRESS)(ConnectInfo->RemoteAddress);
513 	TA->TAAddressCount           = 1;
514 	TA->Address[0].AddressLength = sizeof(TDI_ADDRESS_IP);
515 	TA->Address[0].AddressType   = TDI_ADDRESS_TYPE_IP;
516 	IpAddress           = (PTDI_ADDRESS_IP)(TA->Address[0].Address);
517 	IpAddress->sin_port = WH2N(Port);
518 	IpAddress->in_addr  = DH2N(Address);
519 	Irp = TdiBuildInternalDeviceControlIrp(
520 		TDI_SEND_DATAGRAM,   /* Sub function */
521 		DeviceObject,        /* Device object */
522 		TransportObject,     /* File object */
523 		NULL,                /* Event */
524 		NULL);               /* Return buffer */
525 
526 	if (!Irp)
527 		{
528 			TDI_DbgPrint(MIN_TRACE, ("TdiBuildInternalDeviceControlIrp() failed.\n"));
529 			ExFreePool(ConnectInfo);
530 			return STATUS_INSUFFICIENT_RESOURCES;
531 		}
532 
533 	Mdl = IoAllocateMdl(
534 		Buffer,     /* Virtual address of buffer */
535 		BufferSize, /* Length of buffer */
536 		FALSE,      /* Not secondary */
537 		FALSE,      /* Don't charge quota */
538 		NULL);      /* Don't use IRP */
539 
540 	if (!Mdl)
541 		{
542 			TDI_DbgPrint(MIN_TRACE, ("IoAllocateMdl() failed.\n"));
543 			IoFreeIrp(Irp);
544 			ExFreePool(ConnectInfo);
545 			return STATUS_INSUFFICIENT_RESOURCES;
546 		}
547 
548 	_SEH2_TRY
549 	{
550 		MmProbeAndLockPages(Mdl, KernelMode, IoModifyAccess);
551 	}
552 	_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
553 	{
554 		TDI_DbgPrint(MIN_TRACE, ("MmProbeAndLockPages() failed.\n"));
555 		IoFreeMdl(Mdl);
556 		IoFreeIrp(Irp);
557 		ExFreePool(ConnectInfo);
558 		_SEH2_YIELD(return STATUS_UNSUCCESSFUL);
559 	} _SEH2_END;
560 
561 	TdiBuildSendDatagram(
562 		Irp,               /* I/O Request Packet */
563 		DeviceObject,      /* Device object */
564 		TransportObject,   /* File object */
565 		NULL,              /* Completion routine */
566 		NULL,              /* Completion context */
567 		Mdl,               /* Descriptor for data buffer */
568 		BufferSize,        /* Size of data to send */
569 		ConnectInfo);      /* Connection information */
570 
571 	Status = TdiCall(Irp, DeviceObject, &Iosb, FALSE);
572 
573 	ExFreePool(ConnectInfo);
574 
575 	return Status;
576 }
577 
578 
579 NTSTATUS TdiReceiveDatagram(
580     PFILE_OBJECT TransportObject,
581     USHORT Port,
582     PULONG Address,
583     PUCHAR Buffer,
584     PULONG BufferSize)
585 /*
586  * FUNCTION: Receives a datagram
587  * ARGUMENTS:
588  *     TransportObject = Pointer to transport object
589  *     Port            = Port to receive on
590  *     Address         = Address of buffer to place remote address
591  *     Buffer          = Address of buffer to place received data
592  *     BufferSize      = Address of buffer with length of Buffer (updated)
593  * RETURNS:
594  *     Status of operation
595  */
596 {
597 	PTDI_CONNECTION_INFORMATION ReceiveInfo;
598 	PTDI_CONNECTION_INFORMATION ReturnInfo;
599 	PTA_IP_ADDRESS ReturnAddress;
600 	PDEVICE_OBJECT DeviceObject;
601 	PTDI_ADDRESS_IP IpAddress;
602 	IO_STATUS_BLOCK Iosb;
603 	PVOID MdlBuffer;
604 	NTSTATUS Status;
605 	PIRP Irp;
606 	PMDL Mdl;
607 
608 	DeviceObject = IoGetRelatedDeviceObject(TransportObject);
609 	if (!DeviceObject)
610 		return STATUS_INVALID_PARAMETER;
611 
612 	ReceiveInfo = (PTDI_CONNECTION_INFORMATION) ExAllocatePool(NonPagedPool,
613 		sizeof(TDI_CONNECTION_INFORMATION) +
614 		sizeof(TDI_CONNECTION_INFORMATION) +
615 		sizeof(TA_IP_ADDRESS));
616 
617 	if (!ReceiveInfo)
618 		return STATUS_INSUFFICIENT_RESOURCES;
619 
620 	MdlBuffer = ExAllocatePool(PagedPool, *BufferSize);
621 	if (!MdlBuffer)
622 		return STATUS_INSUFFICIENT_RESOURCES;
623 
624 	RtlZeroMemory(ReceiveInfo, sizeof(TDI_CONNECTION_INFORMATION) + sizeof(TDI_CONNECTION_INFORMATION) +
625 		sizeof(TA_IP_ADDRESS));
626 
627 	RtlCopyMemory(MdlBuffer, Buffer, *BufferSize);
628 
629 	/* Receive from any address */
630 	ReceiveInfo->RemoteAddressLength = 0;
631 	ReceiveInfo->RemoteAddress       = NULL;
632 
633 	ReturnInfo = (PTDI_CONNECTION_INFORMATION) ((PUCHAR)ReceiveInfo + sizeof(TDI_CONNECTION_INFORMATION));
634 	ReturnInfo->RemoteAddressLength = sizeof(TA_IP_ADDRESS);
635 	ReturnInfo->RemoteAddress       = ((PUCHAR)ReturnInfo + sizeof(TDI_CONNECTION_INFORMATION));
636 
637 	ReturnAddress = (PTA_IP_ADDRESS)(ReturnInfo->RemoteAddress);
638 	ReturnAddress->TAAddressCount           = 1;
639 	ReturnAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IP);
640 	ReturnAddress->Address[0].AddressType   = TDI_ADDRESS_TYPE_IP;
641 
642 	IpAddress = (PTDI_ADDRESS_IP)(ReturnAddress->Address[0].Address);
643 	IpAddress->sin_port = WH2N(Port);
644 	IpAddress->in_addr  = DH2N(LocalAddress);
645 
646 	Irp = TdiBuildInternalDeviceControlIrp(
647 		TDI_RECEIVE_DATAGRAM,    /* Sub function */
648 		DeviceObject,            /* Device object */
649 		TransportObject,         /* File object */
650 		NULL,                    /* Event */
651 		NULL);                   /* Return buffer */
652 
653 	if (!Irp)
654 		{
655 			ExFreePool(MdlBuffer);
656 			ExFreePool(ReceiveInfo);
657 			return STATUS_INSUFFICIENT_RESOURCES;
658 		}
659 
660 	Mdl = IoAllocateMdl(
661 		MdlBuffer,      /* Virtual address */
662 		*BufferSize,    /* Length of buffer */
663 		FALSE,          /* Not secondary */
664 		FALSE,          /* Don't charge quota */
665 		NULL);          /* Don't use IRP */
666 
667 	if (!Mdl)
668 		{
669 			IoFreeIrp(Irp);
670 			ExFreePool(MdlBuffer);
671 			ExFreePool(ReceiveInfo);
672 			return STATUS_INSUFFICIENT_RESOURCES;
673 		}
674 
675 	_SEH2_TRY
676 	{
677 		MmProbeAndLockPages(Mdl, KernelMode, IoModifyAccess);
678 	}
679 	_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
680 	{
681 		TDI_DbgPrint(MIN_TRACE, ("MmProbeAndLockPages() failed.\n"));
682 		IoFreeMdl(Mdl);
683 		IoFreeIrp(Irp);
684 		ExFreePool(MdlBuffer);
685 		ExFreePool(ReceiveInfo);
686 		_SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES);
687 	} _SEH2_END;
688 
689 	TdiBuildReceiveDatagram(
690 		Irp,                    /* I/O Request Packet */
691 		DeviceObject,           /* Device object */
692 		TransportObject,        /* File object */
693 		NULL,                   /* Completion routine */
694 		NULL,                   /* Completion context */
695 		Mdl,                    /* Data buffer */
696 		*BufferSize,            /* Size of data buffer */
697 		ReceiveInfo,            /* Connection information */
698 		ReturnInfo,             /* Connection information */
699 		TDI_RECEIVE_NORMAL);    /* Flags */
700 
701 	Status = TdiCall(Irp, DeviceObject, &Iosb, TRUE);
702 
703 	if (NT_SUCCESS(Status))
704 		{
705 			RtlCopyMemory(Buffer, MdlBuffer, Iosb.Information);
706 			*BufferSize = Iosb.Information;
707 			*Address    = DN2H(IpAddress->in_addr);
708 		}
709 
710 	ExFreePool(MdlBuffer);
711 	ExFreePool(ReceiveInfo);
712 
713 	return Status;
714 }
715 
716 
717 VOID TdiSendThread(
718     PVOID Context)
719 /*
720  * FUNCTION: Send thread
721  * ARGUMENTS:
722  *     Context = Pointer to context information
723  * NOTES:
724  *     Transmits an UDP packet every two seconds to ourselves on the chosen port
725  */
726 {
727 	KEVENT Event;
728 	PKEVENT Events[2];
729 	LARGE_INTEGER Timeout;
730 	NTSTATUS Status = STATUS_SUCCESS;
731 	UCHAR Data[40]  = "Testing one, two, three, ...";
732 
733 	if (!OpenError)
734 		{
735 			Timeout.QuadPart = 10000000L;           /* Second factor */
736 			Timeout.QuadPart *= 2;                  /* Number of seconds */
737 			Timeout.QuadPart = -(Timeout.QuadPart); /* Relative time */
738 
739 			KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
740 
741 			Events[0] = &StopEvent;
742 			Events[1] = &Event;
743 
744 			while (NT_SUCCESS(Status))
745 				{
746 					/* Wait until timeout or stop flag is set */
747  					KeWaitForMultipleObjects( 2, (PVOID)Events, WaitAny, Executive, KernelMode, FALSE, &Timeout, NULL);
748 
749 					if (KeReadStateEvent(&StopEvent) != 0)
750 						{
751 							TDI_DbgPrint(MAX_TRACE, ("Received terminate signal...\n"));
752 							break;
753 						}
754 
755 					DbgPrint("Sending data - '%s'\n", Data);
756 
757 					Status = TdiSendDatagram(TdiTransportObject, TEST_PORT, LocalAddress, Data, sizeof(Data));
758 
759 					if (!NT_SUCCESS(Status))
760 						DbgPrint("Failed sending data (Status = 0x%X)\n", Status);
761 				}
762 		}
763 
764 	TDI_DbgPrint(MAX_TRACE, ("Terminating send thread...\n"));
765 
766 	PsTerminateSystemThread(STATUS_SUCCESS);
767 }
768 
769 
770 VOID TdiReceiveThread(
771     PVOID Context)
772 /*
773  * FUNCTION: Receive thread
774  * ARGUMENTS:
775  *     Context = Pointer to context information
776  * NOTES:
777  *     Waits until an UDP packet is received on the chosen endpoint and displays the data
778  */
779 {
780 	ULONG Address;
781 	UCHAR Data[40];
782 	ULONG Size;
783 	NTSTATUS Status = STATUS_SUCCESS;
784 
785 	if (!OpenError)
786 		{
787 			while (NT_SUCCESS(Status))
788 				{
789 					Size = sizeof(Data);
790 					RtlZeroMemory(Data, Size);
791 
792 					Status = TdiReceiveDatagram(TdiTransportObject, TEST_PORT, &Address, Data, &Size);
793 
794 					if (NT_SUCCESS(Status))
795 						{
796 							DbgPrint("Received data - '%s'\n", Data);
797 						}
798 					else
799 						if (Status != STATUS_CANCELLED)
800 							{
801 								TDI_DbgPrint(MIN_TRACE, ("Receive error (Status = 0x%X).\n", Status));
802 							}
803 						else
804 							{
805 								TDI_DbgPrint(MAX_TRACE, ("IRP was cancelled.\n"));
806 							}
807 				}
808 		}
809 
810 	TDI_DbgPrint(MAX_TRACE, ("Terminating receive thread...\n"));
811 
812 	PsTerminateSystemThread(STATUS_SUCCESS);
813 }
814 
815 
816 VOID TdiOpenThread(
817     PVOID Context)
818 /*
819  * FUNCTION: Open thread
820  * ARGUMENTS:
821  *     Context = Pointer to context information (event)
822  */
823 {
824 	NTSTATUS Status;
825 
826 	TDI_DbgPrint(MAX_TRACE, ("Called.\n"));
827 
828 	OpenError = TRUE;
829 
830 	Status = TdiOpenTransport(UDP_DEVICE_NAME, TEST_PORT, &TdiTransport, &TdiTransportObject);
831 
832 	if (NT_SUCCESS(Status))
833 		{
834 			Status = TdiQueryAddress(TdiTransportObject, &LocalAddress);
835 
836 			if (NT_SUCCESS(Status))
837 				{
838 					OpenError = FALSE;
839 					DbgPrint("Using local IP address 0x%X\n", LocalAddress);
840 				}
841 			else
842 				{
843 					TDI_DbgPrint(MIN_TRACE, ("Unable to determine local IP address.\n"));
844 				}
845 			}
846 	else
847 		TDI_DbgPrint(MIN_TRACE, ("Cannot open transport (Status = 0x%X).\n", Status));
848 
849 	TDI_DbgPrint(MAX_TRACE, ("Setting close event.\n"));
850 
851 	KeSetEvent((PKEVENT)Context, 0, FALSE);
852 
853 	TDI_DbgPrint(MIN_TRACE, ("Leaving.\n"));
854 }
855 
856 
857 VOID TdiUnload(
858     PDRIVER_OBJECT DriverObject)
859 /*
860  * FUNCTION: Unload routine
861  * ARGUMENTS:
862  *     DriverObject = Pointer to a driver object for this driver
863  */
864 {
865 	PVOID ReceiveThreadObject = 0;
866 	PVOID SendThreadObject = 0;
867 
868 	TDI_DbgPrint(MAX_TRACE, ("Setting stop flag\n"));
869 
870 	/* Get pointers to the thread objects */
871 	ObReferenceObjectByHandle(SendThread, THREAD_ALL_ACCESS, NULL, KernelMode, &SendThreadObject, NULL);
872 	ObReferenceObjectByHandle(ReceiveThread, THREAD_ALL_ACCESS, NULL, KernelMode, &ReceiveThreadObject, NULL);
873 
874 	KeSetEvent(&StopEvent, 0, FALSE);
875 
876 	/* Wait for send thread to stop */
877 	KeWaitForSingleObject(SendThreadObject, Executive, KernelMode, FALSE, NULL);
878 
879 	/* Wait for receive thread to stop */
880 	KeWaitForSingleObject(ReceiveThreadObject, Executive, KernelMode, FALSE, NULL);
881 
882 	/* Close device */
883 	TdiCloseDevice(TdiTransport, TdiTransportObject);
884 }
885 
886 
887 NTSTATUS
888 NTAPI
889 DriverEntry(
890     PDRIVER_OBJECT DriverObject,
891     PUNICODE_STRING RegistryPath)
892 /*
893  * FUNCTION: Main driver entry point
894  * ARGUMENTS:
895  *     DriverObject = Pointer to a driver object for this driver
896  *     RegistryPath = Registry node for configuration parameters
897  * RETURNS:
898  *     Status of driver initialization
899  */
900 {
901 	KEVENT Event;
902 	NTSTATUS Status;
903 	WORK_QUEUE_ITEM WorkItem;
904 
905 	KeInitializeEvent(&StopEvent, NotificationEvent, FALSE);
906 
907 	/* Call TdiOpenThread() */
908 	KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
909 	ExInitializeWorkItem(&WorkItem, (PWORKER_THREAD_ROUTINE)TdiOpenThread, &Event);
910  	ExQueueWorkItem(&WorkItem, DelayedWorkQueue);
911 	KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, NULL);
912 
913 	/* Create a UDP send thread that sends a dgram every 2 seconds */
914 	Status = PsCreateSystemThread(
915 		&SendThread,                      /* Thread handle */
916 		0,                                /* Desired access */
917 		NULL,                             /* Object attributes */
918 		NULL,                             /* Process handle */
919 		NULL,                             /* Client id */
920 		(PKSTART_ROUTINE)TdiSendThread,   /* Start routine */
921 		NULL);                            /* Start context */
922 
923 	if (!NT_SUCCESS(Status))
924 		{
925 			TDI_DbgPrint(MIN_TRACE, ("PsCreateSystemThread() failed for send thread (Status = 0x%X).\n", Status));
926 			return STATUS_INSUFFICIENT_RESOURCES;
927 		}
928 
929 	/* Create a UDP receive thread */
930 	Status = PsCreateSystemThread(
931 		&ReceiveThread,                       /* Thread handle */
932 		0,                                    /* Desired access */
933 		NULL,                                 /* Object attributes */
934 		NULL,                                 /* Process handle */
935 		NULL,                                 /* Client id */
936 		(PKSTART_ROUTINE)TdiReceiveThread,    /* Start routine */
937 		NULL);                                /* Start context */
938 
939 	if (!NT_SUCCESS(Status))
940 		{
941 			TDI_DbgPrint(MIN_TRACE, ("PsCreateSystemThread() failed for receive thread (Status = 0x%X).\n", Status));
942 			ZwClose(SendThread);
943 			return STATUS_INSUFFICIENT_RESOURCES;
944 		}
945 
946 	DriverObject->DriverUnload = (PDRIVER_UNLOAD)TdiUnload;
947 
948 	return STATUS_SUCCESS;
949 }
950 
951 /* EOF */
952 
953