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