add winpcap 4.0.2 from url http://www.winpcap.org/
[natblaster.git] / winpcap / Packet9x / VXD / Packet.c
blob026ca58bb38b5bbc4a0f9ee482fa896066d70003
1 /*
2 * Copyright (c) 1999 - 2003
3 * NetGroup, Politecnico di Torino (Italy)
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the Politecnico di Torino nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <basedef.h>
35 #include <vmm.h>
36 #include <ndis.h>
37 #include <vwin32.h>
38 #include "debug.h"
39 #include "packet.h"
40 #include <ntddpack.h>
41 #include <vmmreg.h>
42 #pragma VxD_LOCKED_CODE_SEG
43 #pragma VxD_LOCKED_DATA_SEG
45 /*head of the open instances*/
46 PDEVICE_EXTENSION GlobalDeviceExtension = 0;
47 UINT nOpen = 0;
48 POPEN_INSTANCE InstToClose[128];
50 /*number of processes attached to this driver*/
51 int Instances=0;
54 /************************************************************
55 This routine initializes the Packet driver.
56 Arguments:
57 DriverObject - Pointer to driver object created by system.
58 RegistryPath - Pointer to the Unicode name of the registry path
59 for this driver.
60 Return Value:
61 The function value is the final status from the initialization operation.
62 ************************************************************/
63 NTSTATUS
64 DriverEntry( IN PDRIVER_OBJECT DriverObject,
65 IN PUNICODE_STRING RegistryPath
68 NDIS_PROTOCOL_CHARACTERISTICS ProtocolChar;
69 NDIS_STRING ProtoName = NDIS_STRING_CONST("PACKET");
70 NDIS_HANDLE NdisProtocolHandle;
71 NDIS_STATUS Status;
72 TRACE_ENTER( "DriverEntry" );
75 NdisAllocateMemory( (PVOID *)&GlobalDeviceExtension, sizeof( DEVICE_EXTENSION ), 0, -1 );
76 if ( GlobalDeviceExtension != NULL )
78 NdisZeroMemory( (UCHAR*)GlobalDeviceExtension, sizeof(DEVICE_EXTENSION) );
79 NdisZeroMemory( (UCHAR*)&ProtocolChar, sizeof(NDIS_PROTOCOL_CHARACTERISTICS) );
80 ProtocolChar.MajorNdisVersion = 0x03;
81 ProtocolChar.MinorNdisVersion = 0x0A;
82 ProtocolChar.Reserved = 0;
83 ProtocolChar.OpenAdapterCompleteHandler = PacketOpenAdapterComplete;
84 ProtocolChar.CloseAdapterCompleteHandler = PacketUnbindAdapterComplete;
85 ProtocolChar.SendCompleteHandler = PacketSendComplete;
86 ProtocolChar.TransferDataCompleteHandler = PacketTransferDataComplete;
87 ProtocolChar.ResetCompleteHandler = PacketResetComplete;
88 ProtocolChar.RequestCompleteHandler = PacketRequestComplete;
89 ProtocolChar.ReceiveHandler = Packet_tap;
90 ProtocolChar.ReceiveCompleteHandler = PacketReceiveComplete;
91 ProtocolChar.StatusHandler = PacketStatus;
92 ProtocolChar.StatusCompleteHandler = PacketStatusComplete;
93 ProtocolChar.BindAdapterHandler = PacketBindAdapter;
94 ProtocolChar.UnbindAdapterHandler = PacketUnbindAdapter;
95 ProtocolChar.UnloadProtocolHandler = PacketUnload;
96 ProtocolChar.Name = ProtoName;
97 NdisRegisterProtocol( &Status,
98 &GlobalDeviceExtension->NdisProtocolHandle,
99 &ProtocolChar,
100 sizeof(NDIS_PROTOCOL_CHARACTERISTICS) );
101 if (Status != NDIS_STATUS_SUCCESS)
103 NdisFreeMemory( GlobalDeviceExtension, sizeof( DEVICE_EXTENSION ) , 0 );
104 IF_TRACE( "Failed to register protocol with NDIS" );
105 INIT_LEAVE( "DriverEntry" );
106 return Status;
108 /*initializes the list of the open instances*/
109 NdisAllocateSpinLock( &(GlobalDeviceExtension->OpenSpinLock) );
110 InitializeListHead( &GlobalDeviceExtension->OpenList );
111 GlobalDeviceExtension->DriverObject = DriverObject;
113 if(Bind_Names() != NDIS_STATUS_SUCCESS) return NDIS_STATUS_FAILURE;
115 IF_TRACE( "protocol registered with NDIS!!!" );
116 INIT_LEAVE( "DriverEntry" );
117 return Status;
119 IF_TRACE( "Memory Failure" );
121 TRACE_LEAVE( "DriverEntry" );
123 return NDIS_STATUS_RESOURCES;
127 /************************************************************
128 Function used to associate the names of the network devices
129 with the internal NDIS names
130 INPUT:
131 OUTPUT: NDIS_STATUS_SUCCESS if succesful, otherwise NDIS_STATUS_FAILURE
132 ************************************************************/
133 DWORD Bind_Names(void){
134 DWORD res;
135 VMMHKEY Key,Key1;
136 DWORD Klen,Klen1;
137 char NdisName[64];
138 char DevName[64];
139 int i=0;
140 PADAPTER_NAME AName;
142 TRACE_ENTER( "Bind_Names" );
144 // initialize the list of adapter names
145 InitializeListHead( &GlobalDeviceExtension->AdapterNames);
147 // change to HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Class\Net
148 res=VMM_RegOpenKey(HKEY_LOCAL_MACHINE,"SYSTEM",&Key);
149 res=VMM_RegOpenKey(Key,"CURRENTCONTROLSET",&Key);
150 res=VMM_RegOpenKey(Key,"SERVICES",&Key);
151 res=VMM_RegOpenKey(Key,"CLASS",&Key);
152 res=VMM_RegOpenKey(Key,"NET",&Key);
154 if(res!=ERROR_SUCCESS) return NDIS_STATUS_FAILURE;
156 Klen=64;
157 // Scan the list of the adapters
158 while(VMM_RegEnumKey(Key,i,NdisName,Klen)==ERROR_SUCCESS)
160 res=VMM_RegOpenKey(Key,NdisName,&Key1);
161 res=VMM_RegOpenKey(Key1,"NDIS",&Key1);
162 Klen1=64;
163 res=VMM_RegQueryValueEx(Key1,"LOGDRIVERNAME",NULL,NULL, DevName,&Klen1);
164 if(res!=ERROR_SUCCESS){
165 Klen=64;
166 i++;
167 continue;
170 NdisAllocateMemory( (PVOID *)&AName, sizeof(ADAPTER_NAME), 0, -1 );
171 if ( AName == NULL )
173 return NDIS_STATUS_FAILURE;
176 NdisMoveMemory(AName->realname,NdisName,Klen);
177 NdisMoveMemory(AName->devicename,DevName,Klen1);
178 AName->realnamestr.Length=strlen(NdisName);
179 AName->realnamestr.MaximumLength=Klen;
180 AName->realnamestr.Buffer=AName->realname;
182 InsertHeadList( &GlobalDeviceExtension->AdapterNames, &AName->ListElement);
184 Klen=64;
185 i++;
188 TRACE_LEAVE( "Bind_Names" );
190 if(i==0) return NDIS_STATUS_FAILURE;
191 else return NDIS_STATUS_SUCCESS;
195 /************************************************************
196 Callback function called by NDIS when the last insatnce of
197 the packet driver is closed by the capture driver,
198 i.e. when the driver is unloaded
199 INPUT:
200 OUTPUT:
201 ************************************************************/
202 VOID NDIS_API PacketUnload()
204 TRACE_ENTER( "Unload" );
206 TRACE_LEAVE( "Unload" );
207 return;
209 /************************************************************
210 this function returns the descriptor of the adapter from
211 the device ID and process tag
212 INPUT: Name of the adapter to open
213 OUTPUT: instance of the driver
214 ************************************************************/
215 POPEN_INSTANCE GetRunningAdapter(DWORD hDevice,DWORD tagProcess)
217 DWORD dwBytes = 0;
218 DWORD dwSec_Counter=1000; // Or something like that
219 BYTE *lpzName;
220 POPEN_INSTANCE pOpen;
221 PWRAPPER_MAC_BLOCK pWMBlock;
222 PNDIS_MAC_CHARACTERISTICS pNMChar;
223 PLIST_ENTRY pEntry;
224 PLIST_ENTRY pHead;
226 NdisAcquireSpinLock(&GlobalDeviceExtension->OpenSpinLock);
228 pHead = &(GlobalDeviceExtension->OpenList);
229 pOpen = 0;
231 pEntry=pHead->Flink;
235 pOpen = CONTAINING_RECORD( pEntry, OPEN_INSTANCE, ListElement );
236 if((pOpen->hDevice==hDevice)&&(pOpen->tagProcess==tagProcess)){
237 NdisReleaseSpinLock( &GlobalDeviceExtension->OpenSpinLock );
238 return pOpen;
240 pEntry=pEntry->Flink;
241 dwSec_Counter--;
243 }while ((pEntry != pHead)&&(dwSec_Counter));
245 NdisReleaseSpinLock( &GlobalDeviceExtension->OpenSpinLock );
246 return NULL;
249 /************************************************************
250 this function returns the NDIS name of an adapter given its
251 device name
252 INPUT: Name of the adapter to open
253 OUTPUT: instance of the driver
254 ************************************************************/
256 PNDIS_STRING GetNDISAdapterName(BYTE* DeviceName)
258 PADAPTER_NAME pAdap;
259 UINT count=0;
260 PLIST_ENTRY pHead = &(GlobalDeviceExtension->AdapterNames);
261 PLIST_ENTRY pEntry;
263 TRACE_ENTER( "GetNDISAdapterName" );
265 pEntry=pHead->Flink;
267 if(IsListEmpty(pHead)){
268 if(Bind_Names()==NDIS_STATUS_FAILURE)
269 return NULL;
272 do {
273 pAdap = CONTAINING_RECORD( pEntry, ADAPTER_NAME, ListElement );
274 if(compare(pAdap->devicename,DeviceName)==1)return &(pAdap->realnamestr);
275 pEntry=pEntry->Flink;
276 }while (pEntry != pHead || count++>32);
278 TRACE_LEAVE( "GetNDISAdapterName" );
280 return NULL;
283 /************************************************************
284 This function evaluates the length of a string.
285 Useful to avoid the string library functions that are not
286 defined at this level
287 ************************************************************/
288 ULONG
289 strlen( BYTE *s )
291 ULONG len = 0;
292 while ( *s++ ) len++;
293 return len;
297 /************************************************************
298 This function compares two strings
299 ************************************************************/
300 BYTE compare(BYTE *s1,BYTE *s2)
302 TRACE_ENTER( "compare" );
304 while (*s1 && *s2)
306 if (*s1!=*s2) return (BYTE) 0;
308 s1++;
309 s2++;
313 TRACE_LEAVE( "compare" );
315 if ((*s1==0) && (*s2==0)) return (BYTE) 1;
316 else return (BYTE) 0;
319 /************************************************************
320 Return the names of all the MAC drivers on which the driver
321 is attached
322 INPUT: dwDDB e hDevice - parameters coming from the
323 DeviceIOControl procedure, not used here.
324 OUTPUT: pDiocParms - structure containing the returned buffer
325 ************************************************************/
327 DWORD PacketGetMacNameList( DWORD dwDDB,
328 DWORD hDevice,
329 PDIOCPARAMETERS pDiocParms )
331 DWORD dwBytes = 0;
332 BYTE *lpzName;
333 PADAPTER_NAME pAdap;
334 PWRAPPER_MAC_BLOCK pWMBlock;
335 PNDIS_MAC_CHARACTERISTICS pNMChar;
336 ULONG uLength;
338 PLIST_ENTRY pHead = &(GlobalDeviceExtension->AdapterNames);
339 PLIST_ENTRY pEntry;
341 TRACE_ENTER( "PacketGetMacNameList" );
343 pEntry=pHead->Flink;
344 do {
345 pAdap = CONTAINING_RECORD( pEntry, ADAPTER_NAME, ListElement );
346 uLength = strlen( pAdap->devicename );
348 if ( uLength < pDiocParms->cbOutBuffer - dwBytes - 1 )
350 strcat( (BYTE*)(pDiocParms->lpvOutBuffer), pAdap->devicename );
351 strcat( (BYTE*)(pDiocParms->lpvOutBuffer), " " );
352 dwBytes += (uLength + 1);
354 else break;
356 pEntry=pEntry->Flink;
357 }while (pEntry != pHead);
359 *(ULONG*)(pDiocParms->lpcbBytesReturned) = dwBytes;
360 IF_TRACE_MSG( " Bytes Returned: %lu", *(ULONG*)(pDiocParms->lpcbBytesReturned) );
362 TRACE_LEAVE( "PacketGetMacNameList" );
364 return NDIS_STATUS_SUCCESS;
368 /************************************************************
369 This is the dispatch routine for create/open and close requests.
370 These requests complete successfully.
371 INPUT: dwDDB e hDevice - parameters sent by the DeviceIOControl procedure
372 dwService - requested service
373 pDiocParms - structure containing the parameters of the call
374 OUTPUT: the status of the operation
375 ************************************************************/
377 DWORD _stdcall PacketIOControl( DWORD dwService,
378 DWORD dwDDB,
379 DWORD hDevice,
380 PDIOCPARAMETERS pDiocParms )
382 PUCHAR tpointer;
383 int *StatsBuf;
384 PUCHAR prog;
385 ULONG dim,timeout;
386 NDIS_STATUS Status;
387 PPACKET_OID_DATA reqbuff;
388 POPEN_INSTANCE Open,tOpen;
389 PNDIS_STRING str;
390 ULONG mode;
391 PLIST_ENTRY pEntry;
392 PLIST_ENTRY pHead;
393 PADAPTER_NAME AName;
394 UINT i;
395 SHORT timezone;
397 TRACE_ENTER( "DeviceIoControl" );
399 if(!(dwService==IOCTL_PROTOCOL_MACNAME ||
400 dwService==IOCTL_OPEN ||
401 dwService==0)){
402 Open=GetRunningAdapter(hDevice,pDiocParms->tagProcess);
403 if(Open==NULL) return NDIS_STATUS_FAILURE;
406 switch ( dwService )
408 case IOCTL_OPEN: //open message
410 Instances++;
412 //get the NDIS name of current adapter
413 str=GetNDISAdapterName((BYTE*)pDiocParms->lpvInBuffer);
415 if(str==NULL) return NDIS_STATUS_FAILURE;
416 //try to open an instance of the adapter
417 Status = PacketOpen( str, dwDDB, hDevice, pDiocParms);
419 return Status;
422 break;
425 case BIOCGSTATS: //fuction to obtain the capture stats
427 StatsBuf=(int*)pDiocParms->lpvOutBuffer;
428 StatsBuf[0]=Open->Received;
429 StatsBuf[1]=Open->Dropped;
430 *(DWORD *)(pDiocParms->lpcbBytesReturned) = 8;
431 return NDIS_STATUS_SUCCESS;
433 break;
436 case BIOCEVNAME: //fuction to set the shared Event
438 Open->ReadEvent=((DWORD*)pDiocParms->lpvInBuffer)[0];
439 *(DWORD *)(pDiocParms->lpcbBytesReturned) = 0;
441 return NDIS_STATUS_SUCCESS;
443 break;
446 case BIOCSETF: //fuction to set a new bpf filter
448 /*free the previous buffer if it was present*/
449 if(Open->bpfprogram!=NULL){
450 NdisFreeMemory(Open->bpfprogram,Open->bpfprogramlen,0);
451 Open->bpfprogram=NULL; //NULL means accept all
452 Open->bpfprogramlen=0;
455 /*get the pointer to the new program*/
456 prog=(PUCHAR)pDiocParms->lpvInBuffer;
458 /*before accepting the program we must check that it's valid
459 Otherwise, a bogus program could easily crash the system*/
461 Open->bpfprogramlen=pDiocParms->cbInBuffer;
462 if(bpf_validate((struct bpf_insn*)prog,Open->bpfprogramlen/sizeof(struct bpf_insn))==0)
464 Open->bpfprogramlen=0;
465 Open->bpfprogram=NULL;
466 return NDIS_STATUS_FAILURE; // filter not accepted
469 /*allocate the memory to contain the new filter program*/
470 if(NdisAllocateMemory(&Open->bpfprogram,Open->bpfprogramlen, 0, -1 )==NDIS_STATUS_FAILURE)
472 // no memory
473 Open->bpfprogramlen=0;
474 Open->bpfprogram=NULL;
475 return NDIS_STATUS_FAILURE;
478 /*copy the program in the new buffer*/
479 NdisMoveMemory(Open->bpfprogram,prog,Open->bpfprogramlen);
481 /*reset the buffer that could contain packets that don't match the filter*/
482 Open->Bhead=0;
483 Open->Btail=0;
484 Open->BLastByte=0;
485 Open->Received=0;
486 Open->Dropped=0;
488 *(DWORD *)(pDiocParms->lpcbBytesReturned) = Open->bpfprogramlen;
490 break;
493 case BIOCSETBUFFERSIZE: //function to set the dimension of the buffer for the packets
495 /*get the size to allocate*/
496 dim=((PULONG)pDiocParms->lpvInBuffer)[0];
497 /*free the old buffer*/
498 if(Open->Buffer!=NULL)
499 NdisFreeMemory(Open->Buffer,Open->BufSize,0);
501 Open->Buffer=NULL;
502 /*allocate the new buffer*/
504 if(dim>0){
505 NdisAllocateMemory( (PVOID *)&tpointer,dim, 0, -1 );
506 if (tpointer==NULL)
508 // no memory
509 Open->BufSize=0;
510 return NDIS_STATUS_FAILURE;
513 Open->Buffer=tpointer;
516 Open->Bhead=0;
517 Open->Btail=0;
518 Open->BLastByte=0;
519 Open->BufSize=(UINT)dim;
521 *(DWORD *)(pDiocParms->lpcbBytesReturned) = dim;
523 break;
525 case BIOCSMODE:
527 mode=((PULONG)pDiocParms->lpvInBuffer)[0];
528 if(mode==MODE_STAT){
529 Open->mode=MODE_STAT;
530 Open->Nbytes=0;
531 Open->Npackets=0;
533 if(Open->TimeOut==0)Open->TimeOut=1000;
535 else if(mode==MODE_CAPT){
536 Open->mode=MODE_CAPT;
537 return NDIS_STATUS_SUCCESS;
539 else{
540 return NDIS_STATUS_FAILURE;
543 break;
545 case BIOCSRTIMEOUT:
547 timeout=((PULONG)pDiocParms->lpvInBuffer)[0];
548 Open->TimeOut=timeout;
550 *(DWORD *)(pDiocParms->lpcbBytesReturned) = timeout;
552 break;
554 case BIOCSTIMEZONE:
556 timezone=((PSHORT)pDiocParms->lpvInBuffer)[0];
558 Open->StartTime+=((__int64)timezone)*(__int64)1193182*60;
560 *(DWORD *)(pDiocParms->lpcbBytesReturned) = timezone;
562 break;
564 case BIOCSWRITEREP: //set the writes repetition number
566 Open->Nwrites=((PULONG)pDiocParms->lpvInBuffer)[0];
568 *(DWORD *)(pDiocParms->lpcbBytesReturned) = Open->Nwrites;
570 break;
572 case DIOC_CLOSEHANDLE:
573 Status=PacketClose( Open, dwDDB, hDevice, pDiocParms );
575 Instances--;
577 if(Instances<=0)
578 if ( GlobalDeviceExtension )
580 //If any instance is still opened we must close it
581 NdisAcquireSpinLock(&GlobalDeviceExtension->OpenSpinLock);
583 nOpen=0;
585 pHead = &(GlobalDeviceExtension->OpenList);
586 if(pHead!=NULL && !IsListEmpty(pHead))
588 //count the number of open instances
589 pEntry=pHead->Flink;
590 do {
591 tOpen = CONTAINING_RECORD( pEntry, OPEN_INSTANCE, ListElement );
593 pEntry=pEntry->Flink;
594 if(tOpen!=NULL)
595 InstToClose[nOpen++]=tOpen;
597 }while (pEntry != pHead);
599 for(i=0;i<nOpen;i++){
600 PacketClose(InstToClose[i],0,InstToClose[i]->hDevice,NULL);
604 NdisReleaseSpinLock( &GlobalDeviceExtension->OpenSpinLock );
606 //free the names' list
607 pHead = &(GlobalDeviceExtension->AdapterNames);
608 if(pHead!=NULL)
610 while((pEntry=PacketRemoveHeadList(pHead))!=NULL){
611 AName= CONTAINING_RECORD( pEntry, ADAPTER_NAME, ListElement);
612 NdisFreeMemory(AName,sizeof(ADAPTER_NAME),0);
616 //unregister the protocol from NDIS
617 NdisDeregisterProtocol( &Status, GlobalDeviceExtension->NdisProtocolHandle );
619 //free the global device extension
620 NdisFreeMemory(GlobalDeviceExtension,sizeof( DEVICE_EXTENSION ),0);
623 break;
625 case IOCTL_PROTOCOL_RESET:
627 PacketReset( &Status, Open );
629 break;
631 case BIOCQUERYOID:
632 case BIOCSETOID:
633 case IOCTL_PROTOCOL_STATISTICS:
635 return PacketRequest( Open, dwService, dwDDB, hDevice, pDiocParms );
637 case IOCTL_PROTOCOL_READ:
639 return PacketRead( Open, dwDDB, hDevice, pDiocParms );
641 case IOCTL_PROTOCOL_WRITE:
643 return PacketWrite( Open, dwDDB, hDevice, pDiocParms );
645 case IOCTL_PROTOCOL_MACNAME:
647 PacketGetMacNameList( dwDDB, hDevice, pDiocParms );
648 break;
650 default:
651 /*unknown function*/
652 *(DWORD *)(pDiocParms->lpcbBytesReturned) = 0;
653 break;
655 TRACE_LEAVE( "DeviceIoControl" );
657 return NDIS_STATUS_SUCCESS;
662 /************************************************************
663 Function called by NDIS when there is something to communicate
664 to the upper level
665 ************************************************************/
666 VOID
667 PacketStatus(
668 IN NDIS_HANDLE ProtocolBindingContext,
669 IN NDIS_STATUS Status,
670 IN PVOID StatusBuffer,
671 IN UINT StatusBufferSize
674 TRACE_ENTER( "Status Indication" );
675 TRACE_LEAVE( "Status Indication" );
676 return;
679 /************************************************************
680 Complete the previous call
681 ************************************************************/
682 VOID NDIS_API
683 PacketStatusComplete(
684 IN NDIS_HANDLE ProtocolBindingContext
688 TRACE_ENTER( "StatusIndicationComplete" );
689 TRACE_LEAVE( "StatusIndicationComplete" );
690 return;
693 /************************************************************
694 Removes an element from a list.
695 Performs a check to see if the list is empty
696 ************************************************************/
697 PLIST_ENTRY
698 PacketRemoveHeadList(
699 IN PLIST_ENTRY pListHead
702 if ( !IsListEmpty( pListHead ) )
704 PLIST_ENTRY pLE = RemoveHeadList( pListHead );
705 return pLE;
708 return NULL;