2324 lines
58 KiB
C
2324 lines
58 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
netbios.c
|
||
|
||
Abstract:
|
||
|
||
This is the component of netbios that runs in the user process
|
||
passing requests to \Device\Netbios.
|
||
|
||
Author:
|
||
|
||
Colin Watson (ColinW) 15-Mar-91
|
||
|
||
Revision History:
|
||
|
||
Ram Cherala (RamC) 31-Aug-95 Added a try/except around the code which
|
||
calls the post routine in SendAddNcbToDriver
|
||
function. Currently if there is an exception
|
||
in the post routine this thread will die
|
||
before it has a chance to call the
|
||
"AddNameThreadExit" function to decrement
|
||
the trhead count. This will result in not
|
||
being able to shut down the machine without
|
||
hitting the reset switch.
|
||
--*/
|
||
|
||
/*
|
||
Notes:
|
||
|
||
+-----------+ +------------+ +------------+
|
||
| | | | | |
|
||
| User | | User | | Worker |
|
||
| Thread 1 | | Thread 2 | | thread in |
|
||
| | | | | a Post Rtn.|
|
||
+-----+-----+ +-----+------+ +------+-----+
|
||
|Netbios(pncb);|Netbios(pncb); |
|
||
v v |
|
||
+-----+--------------+-----------------+------+
|
||
| -----> Worker | NETAPI.DLL
|
||
| WorkQueue thread |
|
||
| -----> |
|
||
+--------------------+------------------------+
|
||
|
|
||
+---------+---------+
|
||
| |
|
||
| \Device\Netbios |
|
||
| |
|
||
+-------------------+
|
||
|
||
The netbios Worker thread is created automatically by the Netbios call
|
||
when it determines that the user threads are calling Netbios() with
|
||
calls that use a callback routine (called a Post routine in the NetBIOS
|
||
specification).
|
||
|
||
When a worker thread has been created, all requests will be sent via
|
||
the WorkQueue to the worker thread for submission to \Device\Netbios.
|
||
This ensures that send requests go on the wire in the same
|
||
order as the send ncb's are presented. Because the IO system cancels all
|
||
a threads requests when it terminates, the use of the worker thread allows
|
||
such a request inside \Device\Netbios to complete normally.
|
||
|
||
All Post routines are executed by the Worker thread. This allows any Win32
|
||
synchronization mechanisms to be used between the Post routine and the
|
||
applications normal code.
|
||
|
||
The Worker thread terminates when the process exits or when it gets
|
||
an exception such as an access violation.
|
||
|
||
In addition. If the worker thread gets an addname it will create an
|
||
extra thread which will process the addname and then die. This solves
|
||
the problem that the netbios driver will block the users thread during an
|
||
addname (by calling NtCreateFile) even if the caller specified ASYNCH. The
|
||
same code is also used for ASTAT which also creates handles and can take a
|
||
long time now that we support remote adapter status.
|
||
|
||
*/
|
||
|
||
#include <netb.h>
|
||
#include <lmcons.h>
|
||
#include <netlib.h>
|
||
|
||
#if defined(UNICODE)
|
||
#define NETBIOS_SERVICE_NAME L"netbios"
|
||
#else
|
||
#define NETBIOS_SERVICE_NAME "netbios"
|
||
#endif
|
||
|
||
BOOL Initialized;
|
||
|
||
CRITICAL_SECTION Crit; // protects WorkQueue & initialization.
|
||
|
||
LIST_ENTRY WorkQueue; // queue to worker thread.
|
||
|
||
HANDLE Event; // doorbell used when WorkQueue added too.
|
||
|
||
HANDLE WorkerHandle; // Return value when worker thread created.
|
||
HANDLE WaiterHandle; // Return value when waiter thread created.
|
||
|
||
HANDLE NB; // This processes handle to \Device\Netbios.
|
||
|
||
HANDLE ReservedEvent; // Used for synchronous calls
|
||
LONG EventUse; // Prevents simultaneous use of ReservedEvent
|
||
|
||
HANDLE AddNameEvent; // Doorbell used when an AddName worker thread
|
||
// exits.
|
||
volatile LONG AddNameThreadCount;
|
||
|
||
|
||
//
|
||
// Event used to wait for STOP notification from the Kernel mode NETBIOS.SYS
|
||
//
|
||
|
||
HANDLE StopEvent;
|
||
|
||
IO_STATUS_BLOCK StopStatusBlock;
|
||
|
||
|
||
#if AUTO_RESET
|
||
|
||
//
|
||
// Event used to wait for RESET notification from the Kernel mode NETBIOS.SYS
|
||
// Adapters
|
||
//
|
||
|
||
CRITICAL_SECTION ResetCS; // protects access to LanaResetList
|
||
|
||
LIST_ENTRY LanaResetList;
|
||
|
||
NCB OutputNCB;
|
||
|
||
HANDLE LanaResetEvent; // Event signalled when a new adapter is
|
||
// bound to netbios and it needs to be reset
|
||
|
||
IO_STATUS_BLOCK ResetStatusBlock;
|
||
|
||
#endif
|
||
|
||
HMODULE g_hModule;
|
||
|
||
|
||
//
|
||
// netbios command history
|
||
//
|
||
|
||
NCB_INFO g_QueuedHistory[16];
|
||
DWORD g_dwNextQHEntry = 0;
|
||
|
||
NCB_INFO g_DeQueuedHistory[16];
|
||
DWORD g_dwNextDQHEntry = 0;
|
||
|
||
NCB_INFO g_SyncCmdsHistory[16];
|
||
DWORD g_dwNextSCEntry = 0;
|
||
|
||
|
||
|
||
|
||
VOID
|
||
SpinUpAddnameThread(
|
||
IN PNCBI pncb
|
||
);
|
||
|
||
VOID
|
||
AddNameThreadExit(
|
||
VOID
|
||
);
|
||
|
||
DWORD
|
||
SendAddNcbToDriver(
|
||
IN PVOID Context
|
||
);
|
||
|
||
DWORD
|
||
StartNetBIOSDriver(
|
||
VOID
|
||
);
|
||
|
||
#if AUTO_RESET
|
||
VOID
|
||
ResetLanaAndPostListen(
|
||
);
|
||
#endif
|
||
|
||
NTSTATUS
|
||
StartNB(
|
||
OUT OBJECT_ATTRIBUTES *pobjattr,
|
||
IN UNICODE_STRING *punicode,
|
||
OUT IO_STATUS_BLOCK *piosb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is a worker function of Netbios. It will try to start NB
|
||
service.
|
||
|
||
Arguments:
|
||
OUT pobjattr - object attribute
|
||
IN punicode - netbios file name
|
||
OUT piosb - ioblock
|
||
|
||
Return Value:
|
||
|
||
The function value is the status of the operation.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Open a handle to \\Device\Netbios
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
pobjattr, // obj attr to initialize
|
||
punicode, // string to use
|
||
OBJ_CASE_INSENSITIVE, // Attributes
|
||
NULL, // Root directory
|
||
NULL); // Security Descriptor
|
||
|
||
return NtCreateFile(
|
||
&NB, // ptr to handle
|
||
GENERIC_READ // desired...
|
||
| GENERIC_WRITE, // ...access
|
||
pobjattr, // name & attributes
|
||
piosb, // I/O status block.
|
||
NULL, // alloc size.
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
FILE_SHARE_DELETE // share...
|
||
| FILE_SHARE_READ
|
||
| FILE_SHARE_WRITE, // ...access
|
||
FILE_OPEN_IF, // create disposition
|
||
0, // ...options
|
||
NULL, // EA buffer
|
||
0L ); // Ea buffer len
|
||
}
|
||
|
||
|
||
unsigned char APIENTRY
|
||
Netbios(
|
||
IN PNCB pncb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the applications entry point into netapi.dll to support
|
||
netbios 3.0 conformant applications.
|
||
|
||
Arguments:
|
||
|
||
IN PNCB pncb- Supplies the NCB to be processed. Contents of the NCB and
|
||
buffers pointed to by the NCB will be modified in conformance with
|
||
the netbios 3.0 specification.
|
||
|
||
Return Value:
|
||
|
||
The function value is the status of the operation.
|
||
|
||
Notes:
|
||
|
||
The reserved field is used to hold the IO_STATUS_BLOCK.
|
||
|
||
Even if the application specifies ASYNCH, the thread may get blocked
|
||
for a period of time while we open transports, create worker threads
|
||
etc.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// pncbi saves doing lots of type casting. The internal form includes
|
||
// the use of the reserved fields.
|
||
//
|
||
|
||
PNCBI pncbi = (PNCBI) pncb;
|
||
|
||
NTSTATUS ntStatus;
|
||
|
||
BOOL bPending = FALSE;
|
||
|
||
|
||
|
||
if ( ((ULONG_PTR)pncbi & 3) != 0)
|
||
{
|
||
//
|
||
// NCB must be 32 bit aligned
|
||
//
|
||
|
||
pncbi->ncb_retcode = pncbi->ncb_cmd_cplt = NRC_BADDR;
|
||
return NRC_BADDR;
|
||
}
|
||
|
||
|
||
//
|
||
// using this field to fix bug # 293765
|
||
//
|
||
|
||
pncbi-> ncb_reserved = 0;
|
||
|
||
|
||
//
|
||
// Conform to Netbios 3.0 specification by flagging request in progress
|
||
//
|
||
|
||
pncbi->ncb_retcode = pncbi->ncb_cmd_cplt = NRC_PENDING;
|
||
|
||
DisplayNcb( pncbi );
|
||
|
||
if ( !Initialized )
|
||
{
|
||
EnterCriticalSection( &Crit );
|
||
|
||
//
|
||
// Check again to see if another thread got into the critical section
|
||
// and initialized the worker thread.
|
||
//
|
||
|
||
if ( !Initialized )
|
||
{
|
||
|
||
IO_STATUS_BLOCK iosb;
|
||
OBJECT_ATTRIBUTES objattr;
|
||
UNICODE_STRING unicode;
|
||
|
||
HANDLE Threadid;
|
||
BOOL Flag;
|
||
|
||
|
||
//
|
||
// 1. start netbios driver
|
||
//
|
||
|
||
//
|
||
// Open handle to \\Device\Netbios
|
||
//
|
||
|
||
RtlInitUnicodeString( &unicode, NB_DEVICE_NAME);
|
||
|
||
ntStatus = StartNB( &objattr, &unicode, &iosb );
|
||
|
||
if ( !NT_SUCCESS( ntStatus ) )
|
||
{
|
||
//
|
||
// Load the driver
|
||
//
|
||
|
||
DWORD err = 0;
|
||
|
||
err = StartNetBIOSDriver();
|
||
|
||
if ( err )
|
||
{
|
||
pncbi->ncb_retcode = NRC_OPENERR;
|
||
pncbi->ncb_cmd_cplt = NRC_OPENERR;
|
||
|
||
NbPrintf( ( "[NETAPI32] Failed to load driver : %lx\n",
|
||
err ));
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
|
||
else
|
||
{
|
||
//
|
||
// Driver loaded.
|
||
// Open handle to \\Device\Netbios
|
||
//
|
||
|
||
ntStatus = StartNB( &objattr, &unicode, &iosb );
|
||
|
||
if ( !NT_SUCCESS( ntStatus ) )
|
||
{
|
||
pncbi->ncb_retcode = NRC_OPENERR;
|
||
pncbi->ncb_cmd_cplt = NRC_OPENERR;
|
||
|
||
NbPrintf( ( "[NETAPI32] Failed to open handle : %X\n",
|
||
ntStatus ));
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// 2. create a reserved (reusable) event for internal use
|
||
//
|
||
|
||
ntStatus = NtCreateEvent(
|
||
&ReservedEvent, EVENT_ALL_ACCESS,
|
||
NULL, SynchronizationEvent, FALSE
|
||
);
|
||
|
||
if ( !NT_SUCCESS( ntStatus) )
|
||
{
|
||
pncbi->ncb_retcode = NRC_SYSTEM;
|
||
pncbi->ncb_cmd_cplt = NRC_SYSTEM;
|
||
|
||
NbPrintf( ( "[NETAPI32] Failed to create Reserved Event : %X\n",
|
||
ntStatus ) );
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
NtClose( NB );
|
||
NB = NULL;
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
|
||
EventUse = 1;
|
||
|
||
|
||
//
|
||
// Initialize shared datastructures
|
||
//
|
||
|
||
//
|
||
// create a queue for work items to be queued to the Worker thread
|
||
//
|
||
|
||
InitializeListHead( &WorkQueue );
|
||
|
||
|
||
//
|
||
// 4. create an event to communicate with the Worker thread
|
||
//
|
||
|
||
ntStatus = NtCreateEvent(
|
||
&Event, EVENT_ALL_ACCESS,
|
||
NULL, SynchronizationEvent, FALSE
|
||
);
|
||
|
||
if ( !NT_SUCCESS( ntStatus ) )
|
||
{
|
||
pncbi->ncb_retcode = NRC_SYSTEM;
|
||
pncbi->ncb_cmd_cplt = NRC_SYSTEM;
|
||
|
||
NbPrintf( ( "[NETAPI32] Failed to create Event : %X\n",
|
||
ntStatus ) );
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
NtClose( ReservedEvent );
|
||
NtClose( NB );
|
||
NB = NULL;
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
|
||
|
||
//
|
||
// 5.
|
||
// create an event to synchronize ADD name operations with
|
||
// Lana Reset operations. Both these are performed in separate
|
||
// threads and RESET operations are gated by the ADD names
|
||
//
|
||
|
||
ntStatus = NtCreateEvent(
|
||
&AddNameEvent, EVENT_ALL_ACCESS,
|
||
NULL, NotificationEvent, FALSE
|
||
);
|
||
|
||
if ( !NT_SUCCESS( ntStatus ) )
|
||
{
|
||
pncbi->ncb_retcode = NRC_SYSTEM;
|
||
pncbi->ncb_cmd_cplt = NRC_SYSTEM;
|
||
|
||
NbPrintf( ( "[NETAPI32] Failed to create AddName Event : %X\n",
|
||
ntStatus ) );
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
NtClose( Event );
|
||
NtClose( ReservedEvent );
|
||
NtClose( NB );
|
||
NB = NULL;
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
|
||
|
||
//
|
||
// 6. Create an event to register for stop notification.
|
||
//
|
||
|
||
ntStatus = NtCreateEvent(
|
||
&StopEvent,
|
||
EVENT_ALL_ACCESS,
|
||
NULL,
|
||
SynchronizationEvent,
|
||
FALSE
|
||
);
|
||
|
||
if ( !NT_SUCCESS( ntStatus ) )
|
||
{
|
||
pncbi->ncb_retcode = NRC_SYSTEM;
|
||
pncbi->ncb_cmd_cplt = NRC_SYSTEM;
|
||
|
||
NbPrintf( ( "[NETAPI32] Failed to create StopEvent Event : %X\n",
|
||
ntStatus ) );
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
NtClose( AddNameEvent );
|
||
NtClose( Event );
|
||
NtClose( ReservedEvent );
|
||
NtClose( NB );
|
||
NB = NULL;
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
|
||
|
||
#if AUTO_RESET
|
||
|
||
//
|
||
// 7. Create an event to register for reset notification.
|
||
//
|
||
|
||
ntStatus = NtCreateEvent(
|
||
&LanaResetEvent,
|
||
EVENT_ALL_ACCESS,
|
||
NULL,
|
||
SynchronizationEvent,
|
||
FALSE
|
||
);
|
||
|
||
if ( !NT_SUCCESS( ntStatus ) )
|
||
{
|
||
pncbi->ncb_retcode = NRC_SYSTEM;
|
||
pncbi->ncb_cmd_cplt = NRC_SYSTEM;
|
||
|
||
NbPrintf( ( "[NETAPI32] Failed to create StopEvent Event : %X\n",
|
||
ntStatus ) );
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
NtClose( StopEvent );
|
||
NtClose( AddNameEvent );
|
||
NtClose( Event );
|
||
NtClose( ReservedEvent );
|
||
NtClose( NB );
|
||
NB = NULL;
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// 8. create a worker thread to handle async. Netbios requests
|
||
//
|
||
|
||
{
|
||
TCHAR szFileName[MAX_PATH + 1];
|
||
GetModuleFileName(g_hModule, szFileName,
|
||
sizeof(szFileName) / sizeof(TCHAR));
|
||
LoadLibrary(szFileName);
|
||
}
|
||
|
||
WaiterHandle = CreateThread(
|
||
NULL, // Standard thread attributes
|
||
0, // Use same size stack as users
|
||
// application
|
||
NetbiosWaiter,
|
||
// Routine to start in new thread
|
||
0, // Parameter to thread
|
||
0, // No special CreateFlags
|
||
(LPDWORD)&Threadid
|
||
);
|
||
|
||
if ( WaiterHandle == NULL )
|
||
{
|
||
pncbi->ncb_retcode = NRC_SYSTEM;
|
||
pncbi->ncb_cmd_cplt = NRC_SYSTEM;
|
||
|
||
NbPrintf( ( "[NETAPI32] Failed to create Waiter thread" ) );
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
NtClose( StopEvent );
|
||
NtClose( AddNameEvent );
|
||
NtClose( Event );
|
||
NtClose( ReservedEvent );
|
||
NtClose( NB );
|
||
NB = NULL;
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
|
||
NbPrintf( ( "Waiter handle: %lx, threadid %lx\n", WaiterHandle, Threadid ) );
|
||
}
|
||
|
||
Initialized = TRUE;
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Verify that handle to \\Device\Netbios is still open.
|
||
//
|
||
|
||
if ( NB == NULL )
|
||
{
|
||
pncbi->ncb_retcode = NRC_OPENERR;
|
||
pncbi->ncb_cmd_cplt = NRC_OPENERR;
|
||
|
||
NbPrintf( ("[NETAPI32] Netbios service has been stopped\n") );
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
|
||
|
||
//
|
||
// Disallow simultaneous use of both event and callback routine.
|
||
// This will cut down the test cases by disallowing a weird feature.
|
||
//
|
||
|
||
if ( ( ( pncbi->ncb_command & ASYNCH) != 0) &&
|
||
( pncbi->ncb_event) &&
|
||
( pncbi->ncb_post ) )
|
||
{
|
||
pncbi->ncb_retcode = NRC_ILLCMD;
|
||
pncbi->ncb_cmd_cplt = NRC_ILLCMD;
|
||
|
||
NbPrintf( ( "[NETAPI32] Event and Post Routine specified\n" ) );
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// if synchronous command
|
||
//
|
||
|
||
if ( (pncb->ncb_command & ASYNCH) == 0 )
|
||
{
|
||
NTSTATUS Status;
|
||
LONG EventOwned;
|
||
|
||
|
||
// NbPrint( ("[NETAPI32] Synchronpus netbios call\n") );
|
||
|
||
|
||
//
|
||
// Caller wants a synchronous call so ignore ncb_post and ncb_event.
|
||
//
|
||
// We need an event so that we can pause if STATUS_PENDING is returned.
|
||
//
|
||
|
||
EventOwned = InterlockedDecrement( &EventUse );
|
||
|
||
//
|
||
// If EventUse went from 1 to 0 then we obtained ReservedEvent
|
||
//
|
||
|
||
if ( EventOwned == 0)
|
||
{
|
||
pncbi->ncb_event = ReservedEvent;
|
||
}
|
||
else
|
||
{
|
||
InterlockedIncrement( &EventUse );
|
||
|
||
Status = NtCreateEvent(
|
||
&pncbi->ncb_event, EVENT_ALL_ACCESS,
|
||
NULL, SynchronizationEvent,
|
||
FALSE
|
||
);
|
||
|
||
if ( !NT_SUCCESS( Status ) )
|
||
{
|
||
//
|
||
// Failed to create event
|
||
//
|
||
|
||
pncbi->ncb_retcode = NRC_SYSTEM;
|
||
pncbi->ncb_cmd_cplt = NRC_SYSTEM;
|
||
|
||
NbPrintf( ( "[NETAPI32] Failed to create event : %X\n", Status ) );
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
}
|
||
|
||
|
||
pncbi-> ncb_post = NULL;
|
||
|
||
//
|
||
// Check if the worker thread has been created. If it has queue workitem
|
||
// to it. Else use the caller's thread to execute synchronous NCB command
|
||
//
|
||
|
||
if ( WorkerHandle == NULL )
|
||
{
|
||
ADD_SYNCCMD_ENTRY(pncbi);
|
||
|
||
//
|
||
// Worker thread has not been created. Execute in the context of
|
||
// invoker's thread.
|
||
//
|
||
|
||
SendNcbToDriver( pncbi );
|
||
}
|
||
|
||
else
|
||
{
|
||
//
|
||
// Queue Netbios command to worker thread and wait for APC to fire
|
||
//
|
||
|
||
QueueToWorker( pncbi );
|
||
}
|
||
|
||
|
||
do
|
||
{
|
||
ntStatus = NtWaitForSingleObject(
|
||
pncbi->ncb_event, TRUE, NULL
|
||
);
|
||
|
||
} while ( ( ntStatus == STATUS_USER_APC ) ||
|
||
( ntStatus == STATUS_ALERTED) );
|
||
|
||
|
||
ASSERT( ntStatus == STATUS_SUCCESS );
|
||
|
||
if ( !NT_SUCCESS(ntStatus) )
|
||
{
|
||
NbPrintf(( "[NETAPI32] NtWaitForSingleObject failed: %X\n", ntStatus ) );
|
||
|
||
pncbi->ncb_retcode = NRC_SYSTEM;
|
||
pncbi->ncb_cmd_cplt = NRC_SYSTEM;
|
||
}
|
||
|
||
|
||
//
|
||
// release the local event used to wait for
|
||
// completion of netbios command
|
||
//
|
||
|
||
if ( EventOwned == 0)
|
||
{
|
||
InterlockedIncrement( &EventUse );
|
||
}
|
||
else
|
||
{
|
||
NtClose( pncbi->ncb_event );
|
||
}
|
||
|
||
pncbi-> ncb_event = NULL;
|
||
}
|
||
|
||
else
|
||
{
|
||
//
|
||
// Async netbios command. Queue to worker thread
|
||
//
|
||
|
||
//
|
||
// Check if worker exists.
|
||
//
|
||
|
||
if ( WorkerHandle == NULL )
|
||
{
|
||
EnterCriticalSection( &Crit );
|
||
|
||
//
|
||
// verify that worker thread has not been created by
|
||
// while this thread was waiting in EnterCriticalSection
|
||
//
|
||
|
||
if ( WorkerHandle == NULL )
|
||
{
|
||
HANDLE Threadid;
|
||
BOOL Flag;
|
||
|
||
//
|
||
// create a worker thread to handle async. Netbios requests
|
||
//
|
||
|
||
WorkerHandle = CreateThread(
|
||
NULL, // Standard thread attributes
|
||
0, // Use same size stack as users
|
||
// application
|
||
NetbiosWorker,
|
||
// Routine to start in new thread
|
||
0, // Parameter to thread
|
||
0, // No special CreateFlags
|
||
(LPDWORD)&Threadid
|
||
);
|
||
|
||
if ( WorkerHandle == NULL )
|
||
{
|
||
pncbi->ncb_retcode = NRC_SYSTEM;
|
||
pncbi->ncb_cmd_cplt = NRC_SYSTEM;
|
||
|
||
NbPrintf( ( "[NETAPI32] Failed to create Worker thread" ) );
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
|
||
Flag = SetThreadPriority(
|
||
WorkerHandle,
|
||
THREAD_PRIORITY_ABOVE_NORMAL
|
||
);
|
||
|
||
ASSERT( Flag == TRUE );
|
||
|
||
if ( Flag != TRUE )
|
||
{
|
||
NbPrintf(
|
||
("[NETAPI32] Worker SetThreadPriority: %lx\n", GetLastError() )
|
||
);
|
||
}
|
||
|
||
AddNameThreadCount = 0;
|
||
|
||
NbPrintf( ( "Worker handle: %lx, threadid %lx\n", WorkerHandle, Threadid ) );
|
||
}
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
}
|
||
|
||
// NbPrint( ("[NETAPI32] Asynchronpus netbios call\n") );
|
||
|
||
bPending = TRUE;
|
||
QueueToWorker( pncbi );
|
||
}
|
||
|
||
|
||
switch ( pncb->ncb_command & ~ASYNCH )
|
||
{
|
||
case NCBRECV:
|
||
case NCBRECVANY:
|
||
case NCBDGRECV:
|
||
case NCBDGSENDBC:
|
||
case NCBDGRECVBC:
|
||
case NCBENUM:
|
||
case NCBASTAT:
|
||
case NCBSSTAT:
|
||
case NCBCANCEL:
|
||
case NCBCALL:
|
||
DisplayNcb( pncbi );
|
||
}
|
||
|
||
|
||
if ( bPending )
|
||
{
|
||
return NRC_GOODRET;
|
||
}
|
||
else
|
||
{
|
||
return pncbi->ncb_cmd_cplt;
|
||
}
|
||
|
||
} // NetBios
|
||
|
||
|
||
|
||
DWORD
|
||
StartNetBIOSDriver(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Starts the netbios.sys driver using the service controller
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Returns:
|
||
|
||
Error return from service controller.
|
||
|
||
++*/
|
||
{
|
||
|
||
DWORD err = NO_ERROR;
|
||
SC_HANDLE hSC;
|
||
SC_HANDLE hSCService;
|
||
|
||
|
||
hSC = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT );
|
||
|
||
if (hSC == NULL)
|
||
{
|
||
return(GetLastError());
|
||
}
|
||
|
||
hSCService = OpenService( hSC, NETBIOS_SERVICE_NAME, SERVICE_START );
|
||
|
||
if (hSCService == NULL)
|
||
{
|
||
CloseServiceHandle(hSC);
|
||
return(GetLastError());
|
||
}
|
||
|
||
if ( !StartService( hSCService, 0, NULL ) )
|
||
{
|
||
err = GetLastError();
|
||
}
|
||
CloseServiceHandle(hSCService);
|
||
CloseServiceHandle(hSC);
|
||
|
||
|
||
if ( err )
|
||
{
|
||
NbPrintf( ("[NETAPI32] LEAVING StartNetBIOSDriver, Error : %d\n", err) );
|
||
}
|
||
|
||
return(err);
|
||
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
QueueToWorker(
|
||
IN PNCBI pncb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queues an ncb to the worker thread.
|
||
|
||
Arguments:
|
||
|
||
IN PNCBI pncb - Supplies the NCB to be processed. Contents of the NCB and
|
||
buffers pointed to by the NCB will be modified in conformance with
|
||
the netbios 3.0 specification.
|
||
|
||
Return Value:
|
||
|
||
The function value is the status of the operation.
|
||
|
||
--*/
|
||
{
|
||
if ( pncb->ncb_event != NULL ) {
|
||
NtResetEvent( pncb->ncb_event, NULL );
|
||
}
|
||
|
||
EnterCriticalSection( &Crit );
|
||
|
||
if ( pncb-> ncb_reserved == 0 ) {
|
||
InsertTailList( &WorkQueue, &pncb->u.ncb_next );
|
||
pncb-> ncb_reserved = 1;
|
||
|
||
//
|
||
// Note queued distory
|
||
//
|
||
|
||
ADD_QUEUE_ENTRY(pncb);
|
||
}
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
// Make sure the worker is awake to perform the request
|
||
NtSetEvent(Event, NULL);
|
||
}
|
||
|
||
#if _MSC_FULL_VER >= 13008827
|
||
#pragma warning(push)
|
||
#pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
|
||
#endif
|
||
|
||
DWORD
|
||
NetbiosWorker(
|
||
IN LPVOID Parameter
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes ASYNC requests made with the callback interface.
|
||
The reasons for using a seperate thread are:
|
||
|
||
1) If a thread makes an async request and exits while the request
|
||
is outstanding then the request will be cancelled by the IO system.
|
||
|
||
2) A seperate thread must be used so that the users POST routine
|
||
can use normal synchronization APIs to access shared data structures.
|
||
If the users thread is used then deadlock can and will happen.
|
||
|
||
The POST routine operates in the context of the worker thread. There are
|
||
no restrictions on what the POST routine can do. For example it can
|
||
submit another ASYNCH request if desired. It will add it to the queue
|
||
of work and set the event as normal.
|
||
|
||
The worker thread will die when the process terminates.
|
||
|
||
Arguments:
|
||
|
||
IN PULONG Parameter - supplies an unused parameter.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
|
||
while ( TRUE)
|
||
{
|
||
//
|
||
// Wait for a request to be placed onto the work queue.
|
||
//
|
||
|
||
//
|
||
// Must wait alertable so that the Apc (post) routine is called.
|
||
//
|
||
|
||
Status = NtWaitForSingleObject( Event, TRUE, NULL );
|
||
|
||
if ( ( Status == STATUS_SUCCESS ) && ( NB != NULL ) )
|
||
{
|
||
EnterCriticalSection( &Crit );
|
||
|
||
//
|
||
// remove each Netbios request and forward it to the NETBIOS driver
|
||
//
|
||
|
||
while ( !IsListEmpty( &WorkQueue ) )
|
||
{
|
||
PLIST_ENTRY entry;
|
||
PNCBI pncb;
|
||
|
||
entry = RemoveHeadList(&WorkQueue);
|
||
|
||
//
|
||
// Zero out reserved field again
|
||
//
|
||
|
||
entry->Flink = entry->Blink = 0;
|
||
|
||
pncb = CONTAINING_RECORD( entry, NCBI, u.ncb_next );
|
||
|
||
ADD_DEQUEUE_ENTRY(pncb);
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
|
||
// Give ncb to the driver specifying the callers APC routine
|
||
|
||
if ( (pncb->ncb_command & ~ASYNCH) == NCBRESET )
|
||
{
|
||
//
|
||
// We may have threads adding names. Wait until
|
||
// they are complete before submitting the reset.
|
||
// Addnames and resets are rare so this should rarely
|
||
// affect an application.
|
||
//
|
||
|
||
EnterCriticalSection( &Crit );
|
||
|
||
NtResetEvent( AddNameEvent, NULL );
|
||
|
||
while ( AddNameThreadCount != 0 )
|
||
{
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
NtWaitForSingleObject( AddNameEvent, TRUE, NULL );
|
||
|
||
EnterCriticalSection( &Crit );
|
||
|
||
NtResetEvent( AddNameEvent, NULL );
|
||
}
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
}
|
||
|
||
|
||
//
|
||
// SendNcbToDriver must not be in a critical section since the
|
||
// request may block if its a non ASYNCH request.
|
||
//
|
||
|
||
if (( (pncb->ncb_command & ~ASYNCH) != NCBADDNAME ) &&
|
||
( (pncb->ncb_command & ~ASYNCH) != NCBADDGRNAME ) &&
|
||
( (pncb->ncb_command & ~ASYNCH) != NCBASTAT ))
|
||
{
|
||
SendNcbToDriver( pncb );
|
||
}
|
||
else
|
||
{
|
||
SpinUpAddnameThread( pncb );
|
||
}
|
||
|
||
EnterCriticalSection( &Crit );
|
||
|
||
}
|
||
|
||
LeaveCriticalSection( &Crit );
|
||
}
|
||
else
|
||
if ( NB == NULL )
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
|
||
UNREFERENCED_PARAMETER( Parameter );
|
||
}
|
||
|
||
DWORD
|
||
NetbiosWaiter(
|
||
IN LPVOID Parameter
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine pends IOCTLs with the kernel mde component of Netbios and
|
||
wait for them to complete. The reason for a separate thread is that
|
||
these IOCTLs cannot be pended in the context of the user threads as
|
||
exiting the user thread will cause the IOCTL to get cancelled.
|
||
|
||
In addition this thread is created at Netbios initialization (refer
|
||
Netbios function) which could (and is) called from the DLL main of
|
||
applications. So the initialization code cannot wait for this thread
|
||
to be created and initialized due to NT serialization of library
|
||
loads and thread creation.
|
||
|
||
To merge this thread with the Worker thread was deemed risky. To do
|
||
this the worker thread would execute all ASYNC requests and SYNC
|
||
requests would be executed in the context of the user's thread. This
|
||
was a break from the the previous model where the once the Worker
|
||
thread was created all requests (ASYNC and SYNC) would be executed
|
||
in the context of the worker thread. To preserve the previous mode
|
||
of operation a separate wait thread was created. **** There may be
|
||
a better way to do this **** with only one thread but I am not sure.
|
||
|
||
Arguments:
|
||
|
||
IN PULONG Parameter - supplies an unused parameter.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
|
||
#if AUTO_RESET
|
||
|
||
#define POS_STOP 0
|
||
#define POS_RESET 1
|
||
|
||
#endif
|
||
|
||
|
||
NTSTATUS Status;
|
||
|
||
|
||
//
|
||
// Send an IOCTL down to the kernel mode Netbios driver, to register
|
||
// for stop notification. This call should return STATUS_PENDING.
|
||
// The event specified "StopEvent" will be signalled when the netbios
|
||
// driver is being unloaded.
|
||
//
|
||
|
||
Status = NtDeviceIoControlFile(
|
||
NB,
|
||
StopEvent,
|
||
NULL, NULL,
|
||
&StopStatusBlock,
|
||
IOCTL_NB_REGISTER_STOP,
|
||
NULL, 0,
|
||
NULL, 0
|
||
);
|
||
|
||
if ( ( Status != STATUS_PENDING ) &&
|
||
( Status != STATUS_SUCCESS ) )
|
||
{
|
||
NbPrintf(
|
||
("[NETAPI32] : Netbios IOCTL for STOP failed with status %lx\n", Status)
|
||
);
|
||
}
|
||
|
||
|
||
#if AUTO_RESET
|
||
|
||
Status = NtDeviceIoControlFile(
|
||
NB,
|
||
LanaResetEvent,
|
||
NULL, NULL,
|
||
&ResetStatusBlock,
|
||
IOCTL_NB_REGISTER_RESET,
|
||
NULL, 0,
|
||
(PVOID) &OutputNCB, sizeof( NCB )
|
||
);
|
||
|
||
if ( ( Status != STATUS_PENDING ) &&
|
||
( Status != STATUS_SUCCESS ) )
|
||
{
|
||
//
|
||
// Failed to register reset notification.
|
||
//
|
||
|
||
NbPrintf(
|
||
("[NETAPI32] : Netbios : Failed to register Reset event\n" )
|
||
);
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
while ( TRUE )
|
||
{
|
||
|
||
#if AUTO_RESET
|
||
|
||
HANDLE Events[] = { StopEvent, LanaResetEvent };
|
||
|
||
Status = NtWaitForMultipleObjects( 2, Events, WaitAny, TRUE, NULL );
|
||
|
||
if ( Status == POS_STOP )
|
||
{
|
||
Status = NtClose( NB );
|
||
InterlockedExchangePointer( (PVOID *) &NB, NULL );
|
||
|
||
NbPrintf( ("[NETAPI32] Stop event signaled, Status : %lx\n", Status) );
|
||
|
||
}
|
||
|
||
else
|
||
if ( ( Status == POS_RESET ) && (NB != NULL ) )
|
||
{
|
||
NbPrintf( ("[NETAPI32] Reset event signaled\n") );
|
||
|
||
ResetLanaAndPostListen();
|
||
}
|
||
#else
|
||
|
||
Status = NtWaitForSingleObject( StopEvent, TRUE, NULL );
|
||
|
||
if ( Status == STATUS_SUCCESS )
|
||
{
|
||
NbPrintf( ("[NETAPI32] Stop event signaled\n") );
|
||
|
||
NtClose( NB );
|
||
InterlockedExchangePointer( (PVOID *) &NB, NULL );
|
||
}
|
||
#endif
|
||
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
#if _MSC_FULL_VER >= 13008827
|
||
#pragma warning(pop)
|
||
#endif
|
||
|
||
VOID
|
||
SendNcbToDriver(
|
||
IN PNCBI pncb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines the Device Ioctl code to be used to send the
|
||
ncb to \Device\Netbios and then does the call to send the request
|
||
to the driver.
|
||
|
||
Arguments:
|
||
|
||
IN PNCBI pncb - supplies the NCB to be sent to the driver.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS ntstatus;
|
||
|
||
char * buffer;
|
||
unsigned short length;
|
||
|
||
// Use NULL for the buffer if only the NCB is to be passed.
|
||
|
||
switch ( pncb->ncb_command & ~ASYNCH ) {
|
||
case NCBSEND:
|
||
case NCBSENDNA:
|
||
case NCBRECV:
|
||
case NCBRECVANY:
|
||
case NCBDGSEND:
|
||
case NCBDGRECV:
|
||
case NCBDGSENDBC:
|
||
case NCBDGRECVBC:
|
||
case NCBASTAT:
|
||
case NCBFINDNAME:
|
||
case NCBSSTAT:
|
||
case NCBENUM:
|
||
case NCBACTION:
|
||
buffer = pncb->ncb_buffer;
|
||
length = pncb->ncb_length;
|
||
break;
|
||
|
||
case NCBCANCEL:
|
||
// The second buffer points to the NCB to be cancelled.
|
||
buffer = pncb->ncb_buffer;
|
||
length = sizeof(NCB);
|
||
NbPrintf(( "[NETAPI32] Attempting to cancel PNCB: %lx\n", buffer ));
|
||
DisplayNcb( (PNCBI)buffer );
|
||
break;
|
||
|
||
case NCBCHAINSEND:
|
||
case NCBCHAINSENDNA:
|
||
{
|
||
PUCHAR BigBuffer; // Points to the start of BigBuffer, not
|
||
// the start of user data.
|
||
PUCHAR FirstBuffer;
|
||
|
||
//
|
||
// There is nowhere in the NCB to save the address of BigBuffer.
|
||
// The address is needed to free BigBuffer when the transfer is
|
||
// complete. At the start of BigBuffer, 4 bytes are used to store
|
||
// the user supplied ncb_buffer value which is restored later.
|
||
//
|
||
|
||
BigBuffer = RtlAllocateHeap(
|
||
RtlProcessHeap(), 0,
|
||
sizeof(pncb->ncb_buffer) +
|
||
pncb->ncb_length +
|
||
pncb->cu.ncb_chain.ncb_length2);
|
||
|
||
if ( BigBuffer == NULL ) {
|
||
|
||
NbPrintf(( "[NETAPI32] The Netbios BigBuffer Allocation failed: %lx\n",
|
||
pncb->ncb_length + pncb->cu.ncb_chain.ncb_length2));
|
||
pncb->ncb_retcode = NRC_NORES;
|
||
pncb->ncb_cmd_cplt = NRC_NORES;
|
||
pncb->u.ncb_iosb.Status = STATUS_SUCCESS;
|
||
PostRoutineCaller( pncb, &pncb->u.ncb_iosb, 0);
|
||
return;
|
||
}
|
||
|
||
NbPrintf(( "[NETAPI32] BigBuffer Allocation: %lx\n", BigBuffer));
|
||
|
||
// Save users buffer address.
|
||
RtlMoveMemory(
|
||
BigBuffer,
|
||
&pncb->ncb_buffer,
|
||
sizeof(pncb->ncb_buffer));
|
||
|
||
FirstBuffer = pncb->ncb_buffer;
|
||
|
||
pncb->ncb_buffer = BigBuffer;
|
||
|
||
// Copy the user data.
|
||
try {
|
||
|
||
RtlMoveMemory(
|
||
sizeof(pncb->ncb_buffer) + BigBuffer,
|
||
&FirstBuffer[0],
|
||
pncb->ncb_length);
|
||
|
||
RtlMoveMemory(
|
||
sizeof(pncb->ncb_buffer) + BigBuffer + pncb->ncb_length,
|
||
&pncb->cu.ncb_chain.ncb_buffer2[0],
|
||
pncb->cu.ncb_chain.ncb_length2);
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
pncb->ncb_retcode = NRC_BUFLEN;
|
||
pncb->ncb_cmd_cplt = NRC_BUFLEN;
|
||
pncb->u.ncb_iosb.Status = STATUS_SUCCESS;
|
||
ChainSendPostRoutine( pncb, &pncb->u.ncb_iosb, 0);
|
||
return;
|
||
}
|
||
|
||
NbPrintf(( "[NETAPI32] Submit chain send pncb: %lx, event: %lx, post: %lx. \n",
|
||
pncb,
|
||
pncb->ncb_event,
|
||
pncb->ncb_post));
|
||
|
||
ntstatus = NtDeviceIoControlFile(
|
||
NB,
|
||
NULL,
|
||
ChainSendPostRoutine, // APC Routine
|
||
pncb, // APC Context
|
||
&pncb->u.ncb_iosb, // IO Status block
|
||
IOCTL_NB_NCB,
|
||
pncb, // InputBuffer
|
||
sizeof(NCB),
|
||
sizeof(pncb->ncb_buffer) + BigBuffer, // Outputbuffer
|
||
pncb->ncb_length + pncb->cu.ncb_chain.ncb_length2);
|
||
|
||
if ((ntstatus != STATUS_SUCCESS) &&
|
||
(ntstatus != STATUS_PENDING) &&
|
||
(ntstatus != STATUS_HANGUP_REQUIRED)) {
|
||
NbPrintf(( "[NETAPI32] The Netbios Chain Send failed: %X\n", ntstatus ));
|
||
|
||
if ( ntstatus == STATUS_ACCESS_VIOLATION ) {
|
||
pncb->ncb_retcode = NRC_BUFLEN;
|
||
} else {
|
||
pncb->ncb_retcode = NRC_SYSTEM;
|
||
}
|
||
ChainSendPostRoutine( pncb, &pncb->u.ncb_iosb, 0);
|
||
}
|
||
|
||
NbPrintf(( "[NETAPI32] PNCB: %lx completed, status:%lx, ncb_retcode: %#04x\n",
|
||
pncb,
|
||
ntstatus,
|
||
pncb->ncb_retcode ));
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
#if AUTO_RESET
|
||
|
||
//
|
||
// added to fix bug : 170107
|
||
//
|
||
|
||
//
|
||
// Remember the parameters used in reseting a LANA. LANAs need to
|
||
// be automatically re-reset when they get unbound and bound back to
|
||
// netbios.sys. This happens in the case of TCPIP devices that renew
|
||
// their IP addresses.
|
||
//
|
||
|
||
case NCBRESET :
|
||
{
|
||
PRESET_LANA_NCB prlnTmp;
|
||
PLIST_ENTRY ple, pleHead;
|
||
|
||
buffer = NULL;
|
||
length = 0;
|
||
|
||
|
||
NbPrintf( (
|
||
"[NETAPI32] : Netbios : reseting adapter %d\n",
|
||
pncb-> ncb_lana_num
|
||
) );
|
||
|
||
//
|
||
// Add Reset NCB to global list
|
||
//
|
||
|
||
EnterCriticalSection( &ResetCS );
|
||
|
||
//
|
||
// check if already present
|
||
//
|
||
|
||
pleHead = &LanaResetList;
|
||
|
||
for ( ple = pleHead-> Flink; ple != pleHead; ple = ple-> Flink )
|
||
{
|
||
prlnTmp = CONTAINING_RECORD( ple, RESET_LANA_NCB, leList );
|
||
|
||
if ( prlnTmp-> ResetNCB.ncb_lana_num == pncb-> ncb_lana_num )
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
if ( ple == pleHead )
|
||
{
|
||
//
|
||
// NO reset was performed before for this LANA
|
||
//
|
||
|
||
//
|
||
// allocate a NCB entry and copy the NCB used
|
||
//
|
||
|
||
prlnTmp = HeapAlloc(
|
||
GetProcessHeap(), 0, sizeof( RESET_LANA_NCB )
|
||
);
|
||
|
||
if ( prlnTmp == NULL )
|
||
{
|
||
NbPrintf( (
|
||
"[NETAPI32] : Netbios : Failed to allocate RESET_LANA_NCB"
|
||
) );
|
||
|
||
LeaveCriticalSection( &ResetCS );
|
||
|
||
break;
|
||
}
|
||
|
||
|
||
ZeroMemory( prlnTmp, sizeof( RESET_LANA_NCB ) );
|
||
|
||
InitializeListHead( &prlnTmp-> leList );
|
||
|
||
CopyMemory( &prlnTmp-> ResetNCB, pncb, FIELD_OFFSET( NCB, ncb_cmd_cplt ) );
|
||
|
||
InsertTailList( &LanaResetList, &prlnTmp-> leList );
|
||
}
|
||
|
||
else
|
||
{
|
||
//
|
||
// Lana was previously reset. Overwrite old parameters.
|
||
//
|
||
|
||
CopyMemory( &prlnTmp-> ResetNCB, pncb, FIELD_OFFSET( NCB, ncb_cmd_cplt ) );
|
||
}
|
||
|
||
|
||
//
|
||
// clear out event/post completion routine when saving the ResetNCB.
|
||
// When this NCB is used to re-issue the reset command, there is no
|
||
// post completion processing to be done.
|
||
//
|
||
|
||
prlnTmp-> ResetNCB.ncb_event = NULL;
|
||
prlnTmp-> ResetNCB.ncb_post = NULL;
|
||
|
||
//
|
||
// when a reset is re-issued it will always a ASYNC command.
|
||
//
|
||
|
||
prlnTmp-> ResetNCB.ncb_command = pncb-> ncb_command | ASYNCH;
|
||
|
||
|
||
LeaveCriticalSection( &ResetCS );
|
||
|
||
break;
|
||
}
|
||
|
||
#endif
|
||
|
||
default:
|
||
buffer = NULL;
|
||
length = 0;
|
||
break;
|
||
}
|
||
|
||
// NbPrintf(( "[NETAPI32] Submit pncb: %lx, event: %lx, post: %lx. \n",
|
||
// pncb,
|
||
// pncb->ncb_event,
|
||
// pncb->ncb_post));
|
||
|
||
ntstatus = NtDeviceIoControlFile(
|
||
NB,
|
||
NULL,
|
||
PostRoutineCaller, // APC Routine
|
||
pncb, // APC Context
|
||
&pncb->u.ncb_iosb, // IO Status block
|
||
IOCTL_NB_NCB,
|
||
pncb, // InputBuffer
|
||
sizeof(NCB),
|
||
buffer, // Outputbuffer
|
||
length );
|
||
|
||
if ((ntstatus != STATUS_SUCCESS) &&
|
||
(ntstatus != STATUS_PENDING) &&
|
||
(ntstatus != STATUS_HANGUP_REQUIRED)) {
|
||
NbPrintf(( "[NETAPI32] The Netbios NtDeviceIoControlFile failed: %X\n", ntstatus ));
|
||
|
||
NbPrintf(( "[NETAPI32] PNCB: %lx completed, status:%lx, ncb_retcode: %#04x,"
|
||
"ncb_cmd_cmplt: %#04x\n", pncb, ntstatus, pncb->ncb_retcode,
|
||
pncb-> ncb_cmd_cplt ));
|
||
|
||
if ( ntstatus == STATUS_ACCESS_VIOLATION ) {
|
||
pncb->ncb_retcode = NRC_BUFLEN;
|
||
} else {
|
||
pncb->ncb_retcode = NRC_SYSTEM;
|
||
}
|
||
PostRoutineCaller( pncb, &pncb->u.ncb_iosb, 0);
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
VOID
|
||
SpinUpAddnameThread(
|
||
IN PNCBI pncb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Spin up an another thread so that the worker thread does not block while
|
||
the blocking fsctl is being processed.
|
||
|
||
Arguments:
|
||
|
||
IN PNCBI pncb - supplies the NCB to be sent to the driver.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
HANDLE Threadid;
|
||
HANDLE AddNameHandle;
|
||
|
||
EnterCriticalSection( &Crit );
|
||
AddNameThreadCount++;
|
||
NtResetEvent( AddNameEvent, NULL );
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
AddNameHandle = CreateThread(
|
||
NULL, // Standard thread attributes
|
||
0, // Use same size stack as users
|
||
// application
|
||
SendAddNcbToDriver,
|
||
// Routine to start in new thread
|
||
pncb, // Parameter to thread
|
||
0, // No special CreateFlags
|
||
(LPDWORD)&Threadid);
|
||
|
||
if ( AddNameHandle == NULL ) {
|
||
//
|
||
// Wait a couple of seconds just in case this is a burst
|
||
// of addnames and we have run out of resources creating
|
||
// threads. In a couple of seconds one of the other
|
||
// addname threads should complete.
|
||
//
|
||
|
||
Sleep(2000);
|
||
|
||
AddNameHandle = CreateThread(
|
||
NULL, // Standard thread attributes
|
||
0, // Use same size stack as users
|
||
// application
|
||
SendAddNcbToDriver,
|
||
// Routine to start in new thread
|
||
pncb, // Parameter to thread
|
||
0, // No special CreateFlags
|
||
(LPDWORD)&Threadid);
|
||
|
||
if ( AddNameHandle == NULL ) {
|
||
|
||
//
|
||
// Retry failed. Lower the counts to their values prior to
|
||
// calling SpinUpAddNameThread
|
||
//
|
||
|
||
AddNameThreadExit();
|
||
|
||
pncb->ncb_retcode = NRC_NORES;
|
||
NbPrintf(( "[NETAPI32] Create Addname Worker Thread failed\n" ));
|
||
pncb->u.ncb_iosb.Status = STATUS_SUCCESS;
|
||
PostRoutineCaller( pncb, &pncb->u.ncb_iosb, 0);
|
||
} else {
|
||
CloseHandle( AddNameHandle );
|
||
}
|
||
} else {
|
||
CloseHandle( AddNameHandle );
|
||
}
|
||
}
|
||
|
||
VOID
|
||
AddNameThreadExit(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Keep counts accurate so that any resets being processed by the main
|
||
worker thread block appropriately.
|
||
|
||
Arguments:
|
||
|
||
none.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
EnterCriticalSection( &Crit );
|
||
AddNameThreadCount--;
|
||
if (AddNameThreadCount == 0) {
|
||
NtSetEvent(AddNameEvent, NULL);
|
||
}
|
||
LeaveCriticalSection( &Crit );
|
||
}
|
||
|
||
DWORD
|
||
SendAddNcbToDriver(
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to post an addname or adapter status ensuring
|
||
that the worker thread does not block.
|
||
|
||
Arguments:
|
||
|
||
IN PVOID Context - supplies the NCB to be sent to the driver.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PNCBI pncb = (PNCBI) Context;
|
||
void (CALLBACK *post)( struct _NCB * );
|
||
HANDLE event;
|
||
HANDLE LocalEvent;
|
||
UCHAR command;
|
||
NTSTATUS ntstatus;
|
||
char * buffer;
|
||
unsigned short length;
|
||
|
||
try {
|
||
command = pncb->ncb_command;
|
||
post = pncb->ncb_post;
|
||
event = pncb->ncb_event;
|
||
|
||
ntstatus = NtCreateEvent( &LocalEvent,
|
||
EVENT_ALL_ACCESS,
|
||
NULL,
|
||
SynchronizationEvent,
|
||
FALSE );
|
||
|
||
if ( !NT_SUCCESS(ntstatus) ) {
|
||
pncb->ncb_retcode = NRC_NORES;
|
||
NbPrintf(( "[NETAPI32] Could not create event\n" ));
|
||
pncb->u.ncb_iosb.Status = STATUS_SUCCESS;
|
||
PostRoutineCaller( pncb, &pncb->u.ncb_iosb, 0);
|
||
AddNameThreadExit();
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// While the NCB is submitted the driver can modify the contents
|
||
// of the NCB. We will ensure that this thread waits until the addname
|
||
// completes before it exits.
|
||
//
|
||
|
||
pncb->ncb_command = pncb->ncb_command & ~ASYNCH;
|
||
|
||
if ( pncb->ncb_command == NCBASTAT ) {
|
||
|
||
buffer = pncb->ncb_buffer;
|
||
length = pncb->ncb_length;
|
||
|
||
} else {
|
||
|
||
ASSERT( (pncb->ncb_command == NCBADDNAME) ||
|
||
(pncb->ncb_command == NCBADDGRNAME) ||
|
||
(pncb->ncb_command == NCBASTAT) );
|
||
|
||
buffer = NULL;
|
||
length = 0;
|
||
}
|
||
|
||
ntstatus = NtDeviceIoControlFile(
|
||
NB,
|
||
LocalEvent,
|
||
NULL, // APC Routine
|
||
NULL, // APC Context
|
||
&pncb->u.ncb_iosb, // IO Status block
|
||
IOCTL_NB_NCB,
|
||
pncb, // InputBuffer
|
||
sizeof(NCB),
|
||
buffer, // Outputbuffer
|
||
length );
|
||
|
||
if ((ntstatus != STATUS_SUCCESS) &&
|
||
(ntstatus != STATUS_PENDING) &&
|
||
(ntstatus != STATUS_HANGUP_REQUIRED)) {
|
||
NbPrintf(( "[NETAPI32] The Netbios NtDeviceIoControlFile failed: %X\n", ntstatus ));
|
||
|
||
if ( ntstatus == STATUS_ACCESS_VIOLATION ) {
|
||
pncb->ncb_retcode = NRC_BUFLEN;
|
||
} else {
|
||
pncb->ncb_retcode = NRC_SYSTEM;
|
||
}
|
||
} else {
|
||
do {
|
||
ntstatus = NtWaitForSingleObject(
|
||
LocalEvent,
|
||
TRUE,
|
||
NULL );
|
||
|
||
} while ( (ntstatus == STATUS_USER_APC) ||
|
||
(ntstatus == STATUS_ALERTED) );
|
||
|
||
ASSERT(ntstatus == STATUS_SUCCESS);
|
||
}
|
||
|
||
pncb->ncb_command = command;
|
||
|
||
// Set the flag that indicates that the NCB is now completed.
|
||
pncb->ncb_cmd_cplt = pncb->ncb_retcode;
|
||
|
||
// Allow application/worker thread to proceed.
|
||
if ( event != NULL ) {
|
||
NtSetEvent( event, NULL );
|
||
}
|
||
|
||
// If the user supplied a post routine then call it.
|
||
if (( post != NULL ) &&
|
||
( (command & ASYNCH) != 0 )) {
|
||
(*(post))( (PNCB)pncb );
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
NbPrintf(( "[NETAPI32] Netbios: Access Violation post processing NCB %lx\n", pncb ));
|
||
NbPrintf(( "[NETAPI32] Netbios: Probable application error\n" ));
|
||
}
|
||
|
||
NtClose( LocalEvent );
|
||
|
||
AddNameThreadExit();
|
||
|
||
ExitThread(0);
|
||
return 0;
|
||
}
|
||
|
||
|
||
VOID
|
||
PostRoutineCaller(
|
||
PVOID Context,
|
||
PIO_STATUS_BLOCK Status,
|
||
ULONG Reserved
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is supplied by SendNcbToDriver to the Io system when
|
||
a Post routine is to be called directly.
|
||
|
||
Arguments:
|
||
|
||
IN PVOID Context - supplies the NCB post routine to be called.
|
||
|
||
IN PIO_STATUS_BLOCK Status.
|
||
|
||
IN ULONG Reserved.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
PNCBI pncbi = (PNCBI) Context;
|
||
void (CALLBACK *post)( struct _NCB * );
|
||
HANDLE event;
|
||
UCHAR command;
|
||
|
||
try {
|
||
|
||
if ( Status->Status == STATUS_HANGUP_REQUIRED ) {
|
||
HangupConnection( pncbi );
|
||
}
|
||
|
||
//
|
||
// Save the command, post routine and the handle to the event so that if the other thread is
|
||
// polling the cmd_cplt flag or the event awaiting completion and immediately trashes
|
||
// the NCB, we behave appropriately.
|
||
//
|
||
post = pncbi->ncb_post;
|
||
event = pncbi->ncb_event;
|
||
command = pncbi->ncb_command;
|
||
|
||
// Set the flag that indicates that the NCB is now completed.
|
||
pncbi->ncb_cmd_cplt = pncbi->ncb_retcode;
|
||
|
||
//
|
||
// NCB may be queued again
|
||
//
|
||
|
||
EnterCriticalSection( &Crit );
|
||
pncbi->ncb_reserved = 0;
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
// Allow application/worker thread to proceed.
|
||
if ( event != NULL ) {
|
||
NtSetEvent( event, NULL );
|
||
}
|
||
|
||
// If the user supplied a post routine then call it.
|
||
if (( post != NULL ) &&
|
||
( (command & ASYNCH) != 0 )) {
|
||
(*(post))( (PNCB)pncbi );
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
NbPrintf(( "[NETAPI32] Netbios: Access Violation post processing NCB %lx\n", pncbi ));
|
||
NbPrintf(( "[NETAPI32] Netbios: Probable application error\n" ));
|
||
}
|
||
|
||
UNREFERENCED_PARAMETER( Reserved );
|
||
}
|
||
|
||
VOID
|
||
ChainSendPostRoutine(
|
||
PVOID Context,
|
||
PIO_STATUS_BLOCK Status,
|
||
ULONG Reserved
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is supplied by SendNcbToDriver to the Io system when
|
||
a chain send ncb is being processed. When the send is complete,
|
||
this routine deletes the BigBuffer used to hold the two parts of
|
||
the chain send. It then calls a post routine if the user supplied one.
|
||
|
||
Arguments:
|
||
|
||
IN PVOID Context - supplies the NCB post routine to be called.
|
||
|
||
IN PIO_STATUS_BLOCK Status.
|
||
|
||
IN ULONG Reserved.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
PNCBI pncbi = (PNCBI) Context;
|
||
PUCHAR BigBuffer;
|
||
void (CALLBACK *post)( struct _NCB * );
|
||
HANDLE event;
|
||
UCHAR command;
|
||
|
||
BigBuffer = pncbi->ncb_buffer;
|
||
|
||
try {
|
||
|
||
// Restore the users NCB contents.
|
||
RtlMoveMemory(
|
||
&pncbi->ncb_buffer,
|
||
BigBuffer,
|
||
sizeof(pncbi->ncb_buffer));
|
||
|
||
NbPrintf(( "[NETAPI32] ChainSendPostRoutine PNCB: %lx, Status: %X\n", pncbi, Status->Status ));
|
||
DisplayNcb( pncbi );
|
||
|
||
if ( Status->Status == STATUS_HANGUP_REQUIRED ) {
|
||
HangupConnection( pncbi );
|
||
}
|
||
|
||
//
|
||
// Save the command, post routine and the handle to the event so that if the other thread is
|
||
// polling the cmd_cplt flag or the event awaiting completion and immediately trashes
|
||
// the NCB, we behave appropriately.
|
||
//
|
||
post = pncbi->ncb_post;
|
||
event = pncbi->ncb_event;
|
||
command = pncbi->ncb_command;
|
||
|
||
// Set the flag that indicates that the NCB is now completed.
|
||
pncbi->ncb_cmd_cplt = pncbi->ncb_retcode;
|
||
|
||
//
|
||
// NCB may be queued again
|
||
//
|
||
|
||
EnterCriticalSection( &Crit );
|
||
pncbi->ncb_reserved = 0;
|
||
LeaveCriticalSection( &Crit );
|
||
|
||
// Allow application/worker thread to proceed.
|
||
if ( event != NULL ) {
|
||
NtSetEvent(event, NULL);
|
||
}
|
||
|
||
// If the user supplied a post routine then call it.
|
||
if (( post != NULL ) &&
|
||
( (command & ASYNCH) != 0 )) {
|
||
(*(post))( (PNCB)pncbi );
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
NbPrintf(( "[NETAPI32] Netbios: Access Violation post processing NCB %lx\n", pncbi ));
|
||
NbPrintf(( "[NETAPI32] Netbios: Probable application error\n" ));
|
||
}
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, BigBuffer);
|
||
|
||
|
||
UNREFERENCED_PARAMETER( Reserved );
|
||
}
|
||
|
||
VOID
|
||
HangupConnection(
|
||
PNCBI pUserNcb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine generates a hangup for the connection. This allows orderly
|
||
cleanup of the connection block in the driver.
|
||
|
||
The return value from the hangup is not used. If the hangup overlaps with
|
||
a reset or a hangup then the hangup will have no effect.
|
||
|
||
The user application is unaware that this operation is being performed.
|
||
|
||
Arguments:
|
||
|
||
IN PNCBI pUserNcb - Identifies the connection to be hung up.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
NCBI ncbi;
|
||
NTSTATUS Status;
|
||
|
||
RtlZeroMemory( &ncbi, sizeof (NCB) );
|
||
ncbi.ncb_command = NCBHANGUP;
|
||
ncbi.ncb_lsn = pUserNcb->ncb_lsn;
|
||
ncbi.ncb_lana_num = pUserNcb->ncb_lana_num;
|
||
ncbi.ncb_retcode = ncbi.ncb_cmd_cplt = NRC_PENDING;
|
||
|
||
Status = NtCreateEvent( &ncbi.ncb_event,
|
||
EVENT_ALL_ACCESS,
|
||
NULL,
|
||
SynchronizationEvent,
|
||
FALSE );
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
//
|
||
// Failed to create event. Cleanup of the Cb will have to wait until
|
||
// the user decides to do another request or exits.
|
||
//
|
||
NbPrintf(( "[NETAPI32] Hangup Session PNCBI: %lx failed to create event!\n" ));
|
||
return;
|
||
}
|
||
|
||
Status = NtDeviceIoControlFile(
|
||
NB,
|
||
ncbi.ncb_event,
|
||
NULL, // APC Routine
|
||
NULL, // APC Context
|
||
&ncbi.u.ncb_iosb, // IO Status block
|
||
IOCTL_NB_NCB,
|
||
&ncbi, // InputBuffer
|
||
sizeof(NCB),
|
||
NULL, // Outputbuffer
|
||
0 );
|
||
|
||
//
|
||
// We must always wait to allow the Apc to fire
|
||
//
|
||
|
||
do {
|
||
Status = NtWaitForSingleObject(
|
||
ncbi.ncb_event,
|
||
TRUE,
|
||
NULL );
|
||
|
||
} while ( (Status == STATUS_USER_APC) ||
|
||
(Status == STATUS_ALERTED) );
|
||
|
||
ASSERT(Status == STATUS_SUCCESS);
|
||
|
||
if (! NT_SUCCESS(Status)) {
|
||
NbPrintf(( "[NETAPI32] The Netbios NtWaitForSingleObject failed: %X\n", Status ));
|
||
}
|
||
|
||
NtClose( ncbi.ncb_event );
|
||
|
||
}
|
||
|
||
VOID
|
||
NetbiosInitialize(
|
||
HMODULE hModule
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called each time a process that uses netapi.dll
|
||
starts up.
|
||
|
||
Arguments:
|
||
|
||
IN HMODULE hModule - Handle to module instance (netapi32.dll)
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
|
||
Initialized = FALSE;
|
||
WorkerHandle = NULL;
|
||
InitializeCriticalSection( &Crit );
|
||
|
||
#if AUTO_RESET
|
||
|
||
InitializeCriticalSection( &ResetCS );
|
||
InitializeListHead( &LanaResetList );
|
||
RtlZeroMemory( &OutputNCB, sizeof( NCB ) );
|
||
|
||
#endif
|
||
|
||
g_hModule = hModule;
|
||
|
||
}
|
||
|
||
VOID
|
||
NetbiosDelete(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called each time a process that uses netapi.dll
|
||
Exits. It resets all lana numbers that could have been used by this
|
||
process. This will cause all Irp's in the system to be completed
|
||
because all the Connection and Address handles will be closed tidily.
|
||
|
||
Arguments:
|
||
|
||
none.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
|
||
#if AUTO_RESET
|
||
|
||
PLIST_ENTRY ple;
|
||
PRESET_LANA_NCB prln;
|
||
|
||
while ( !IsListEmpty( &LanaResetList ) )
|
||
{
|
||
ple = RemoveHeadList( &LanaResetList );
|
||
|
||
prln = CONTAINING_RECORD( ple, RESET_LANA_NCB, leList );
|
||
|
||
HeapFree( GetProcessHeap(), 0, prln );
|
||
}
|
||
|
||
DeleteCriticalSection( &ResetCS );
|
||
|
||
#endif
|
||
|
||
DeleteCriticalSection( &Crit );
|
||
if ( Initialized == FALSE ) {
|
||
// This process did not use Netbios.
|
||
return;
|
||
}
|
||
|
||
NtClose(NB);
|
||
}
|
||
|
||
|
||
|
||
#if AUTO_RESET
|
||
|
||
VOID
|
||
ResetLanaAndPostListen(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked in response to new LANA being indicated to
|
||
NETBIOS.SYS. When this occurs the IOCTL posted by this user-mode
|
||
component of netbios (to listen for new LANA indications) is completed.
|
||
In response the new LANA is reset if it had previously been reset.
|
||
|
||
In addition the routine re-posts the listen to the kernel mode
|
||
component of netbios (NETBIOS.SYS). An exception to this is if
|
||
the LANA number to be reset is 255 ( MAX_LANA + 1 ). This is a
|
||
special case that indicates the NETBIOS.SYS is stopping and listen
|
||
should not be reposted in this case.
|
||
|
||
|
||
Arguments:
|
||
|
||
none.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
PRESET_LANA_NCB prln;
|
||
PLIST_ENTRY ple, pleHead;
|
||
|
||
|
||
NbPrintf( ("[NETAPI32] : Netbios : Entered ResetLanaAndPostListen \n") );
|
||
|
||
|
||
//
|
||
// Check if the LANA number is valid.
|
||
//
|
||
|
||
if ( OutputNCB.ncb_lana_num != ( MAX_LANA + 1 ) )
|
||
{
|
||
EnterCriticalSection( &ResetCS );
|
||
|
||
//
|
||
// find which lana needs a reset
|
||
//
|
||
|
||
NbPrintf( (
|
||
"[NETAPI32] : Netbios : Looking for Lana %d\n", OutputNCB.ncb_lana_num
|
||
) );
|
||
|
||
|
||
pleHead = &LanaResetList;
|
||
|
||
for ( ple = pleHead-> Flink; ple != pleHead; ple = ple-> Flink )
|
||
{
|
||
prln = CONTAINING_RECORD( ple, RESET_LANA_NCB, leList );
|
||
|
||
if ( prln-> ResetNCB.ncb_lana_num == OutputNCB.ncb_lana_num )
|
||
{
|
||
//
|
||
// found Lana that needs reseting
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// if found send reset
|
||
//
|
||
|
||
if ( ple != pleHead )
|
||
{
|
||
//
|
||
// Send Reset to NETBIOS.SYS
|
||
//
|
||
|
||
QueueToWorker( (PNCBI) &prln-> ResetNCB );
|
||
}
|
||
|
||
else
|
||
{
|
||
NbPrintf( (
|
||
"[NETAPI32] : Netbios : Lana %d not found\n",
|
||
OutputNCB.ncb_lana_num
|
||
) );
|
||
}
|
||
|
||
LeaveCriticalSection( &ResetCS );
|
||
|
||
OutputNCB.ncb_lana_num = 0;
|
||
|
||
|
||
//
|
||
// post listen again
|
||
//
|
||
|
||
Status = NtDeviceIoControlFile(
|
||
NB,
|
||
LanaResetEvent,
|
||
NULL, NULL,
|
||
&ResetStatusBlock,
|
||
IOCTL_NB_REGISTER_RESET,
|
||
NULL, 0,
|
||
(PVOID) &OutputNCB, sizeof( NCB )
|
||
);
|
||
|
||
if ( ( Status != STATUS_PENDING ) &&
|
||
( Status != STATUS_SUCCESS ) )
|
||
{
|
||
//
|
||
// Failed to register reset notification.
|
||
//
|
||
|
||
NbPrintf(
|
||
("[NETAPI32] : Netbios : Failed to register Reset event\n" )
|
||
);
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
NbPrintf( (
|
||
"[NETAPI32] : Netbios : LANA 255 indicated, no Listen posted\n"
|
||
) )
|
||
}
|
||
|
||
NbPrintf( ("[NETAPI32] : Netbios : Leaving ResetLanaAndPostListen \n") );
|
||
}
|
||
|
||
#endif
|
||
|