From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/net/netbios/debug.c | 369 ++++++++++ private/net/netbios/makefile | 6 + private/net/netbios/netb.h | 134 ++++ private/net/netbios/netbios.c | 1582 ++++++++++++++++++++++++++++++++++++++++ private/net/netbios/netbios.rc | 11 + private/net/netbios/sources | 62 ++ private/net/netbios/tnetbios.c | 343 +++++++++ private/net/netbios/tnetbl.c | 269 +++++++ private/net/netbios/tnetcall.c | 491 +++++++++++++ 9 files changed, 3267 insertions(+) create mode 100644 private/net/netbios/debug.c create mode 100644 private/net/netbios/makefile create mode 100644 private/net/netbios/netb.h create mode 100644 private/net/netbios/netbios.c create mode 100644 private/net/netbios/netbios.rc create mode 100644 private/net/netbios/sources create mode 100644 private/net/netbios/tnetbios.c create mode 100644 private/net/netbios/tnetbl.c create mode 100644 private/net/netbios/tnetcall.c (limited to 'private/net/netbios') diff --git a/private/net/netbios/debug.c b/private/net/netbios/debug.c new file mode 100644 index 000000000..f6340b67c --- /dev/null +++ b/private/net/netbios/debug.c @@ -0,0 +1,369 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + debug.c + +Abstract: + + This component of netbios runs in the user process and can ( when + built in a debug kernel) will log to either the console or through the + kernel debugger. + +Author: + + Colin Watson (ColinW) 24-Jun-91 + +Revision History: + +--*/ + + +#if DBG + +#include +#include +#include + +ULONG NbDllDebug = 0x0; +#define NB_DLL_DEBUG_NCB 0x00000001 // print all NCB's submitted +#define NB_DLL_DEBUG_NCB_BUFF 0x00000002 // print buffers for NCB's submitted + +BOOL UseConsole = FALSE; +BOOL UseLogFile = TRUE; +HANDLE LogFile = INVALID_HANDLE_VALUE; +#define LOGNAME (LPTSTR) TEXT("netbios.log") + +LONG NbMaxDump = 128; + +// Macro used in DisplayNcb +#define DISPLAY_COMMAND( cmd ) \ + case cmd: NbPrintf(( #cmd )); break; + +VOID +FormattedDump( + PCHAR far_p, + LONG len + ); + +VOID +HexDumpLine( + PCHAR pch, + ULONG len, + PCHAR s, + PCHAR t + ); + +VOID +DisplayNcb( + IN PNCBI pncbi + ) +/*++ + +Routine Description: + + This routine displays on the standard output stream the contents + of the Ncb. + +Arguments: + + IN PNCBI - Supplies the NCB to be displayed. + +Return Value: + + none. + +--*/ +{ + if ( (NbDllDebug & NB_DLL_DEBUG_NCB) == 0 ) { + return; + } + + NbPrintf(( "PNCB %#010lx\n", pncbi)); + + NbPrintf(( "ncb_command %#04x ", pncbi->ncb_command)); + switch ( pncbi->ncb_command & ~ASYNCH ) { + DISPLAY_COMMAND( NCBCALL ); + DISPLAY_COMMAND( NCBLISTEN ); + DISPLAY_COMMAND( NCBHANGUP ); + DISPLAY_COMMAND( NCBSEND ); + DISPLAY_COMMAND( NCBRECV ); + DISPLAY_COMMAND( NCBRECVANY ); + DISPLAY_COMMAND( NCBCHAINSEND ); + DISPLAY_COMMAND( NCBDGSEND ); + DISPLAY_COMMAND( NCBDGRECV ); + DISPLAY_COMMAND( NCBDGSENDBC ); + DISPLAY_COMMAND( NCBDGRECVBC ); + DISPLAY_COMMAND( NCBADDNAME ); + DISPLAY_COMMAND( NCBDELNAME ); + DISPLAY_COMMAND( NCBRESET ); + DISPLAY_COMMAND( NCBASTAT ); + DISPLAY_COMMAND( NCBSSTAT ); + DISPLAY_COMMAND( NCBCANCEL ); + DISPLAY_COMMAND( NCBADDGRNAME ); + DISPLAY_COMMAND( NCBENUM ); + DISPLAY_COMMAND( NCBUNLINK ); + DISPLAY_COMMAND( NCBSENDNA ); + DISPLAY_COMMAND( NCBCHAINSENDNA ); + DISPLAY_COMMAND( NCBLANSTALERT ); + DISPLAY_COMMAND( NCBFINDNAME ); + + // Extensions + DISPLAY_COMMAND( NCALLNIU ); + DISPLAY_COMMAND( NCBQUICKADDNAME ); + DISPLAY_COMMAND( NCBQUICKADDGRNAME ); + DISPLAY_COMMAND( NCBACTION ); + + default: NbPrintf(( " Unknown type")); break; + } + if ( pncbi->ncb_command & ASYNCH ) { + NbPrintf(( " | ASYNCH")); + } + + + NbPrintf(( "\nncb_retcode %#04x\n", pncbi->ncb_retcode)); + NbPrintf(( "ncb_lsn %#04x\n", pncbi->ncb_lsn)); + NbPrintf(( "ncb_num %#04x\n", pncbi->ncb_num)); + + NbPrintf(( "ncb_buffer %#010lx\n",pncbi->ncb_buffer)); + NbPrintf(( "ncb_length %#06x\n", pncbi->ncb_length)); + + NbPrintf(( "\nncb_callname and ncb->name\n")); + FormattedDump( pncbi->cu.ncb_callname, NCBNAMSZ ); + FormattedDump( pncbi->ncb_name, NCBNAMSZ ); + + if (((pncbi->ncb_command & ~ASYNCH) == NCBCHAINSEND) || + ((pncbi->ncb_command & ~ASYNCH) == NCBCHAINSENDNA)) { + NbPrintf(( "ncb_length2 %#06x\n", pncbi->cu.ncb_chain.ncb_length2)); + NbPrintf(( "ncb_buffer2 %#010lx\n",pncbi->cu.ncb_chain.ncb_buffer2)); + } + + NbPrintf(( "ncb_rto %#04x\n", pncbi->ncb_rto)); + NbPrintf(( "ncb_sto %#04x\n", pncbi->ncb_sto)); + NbPrintf(( "ncb_post %lx\n", pncbi->ncb_post)); + NbPrintf(( "ncb_lana_num %#04x\n", pncbi->ncb_lana_num)); + NbPrintf(( "ncb_cmd_cplt %#04x\n", pncbi->ncb_cmd_cplt)); + + NbPrintf(( "ncb_reserve\n")); + FormattedDump( ((PNCB)pncbi)->ncb_reserve, 14 ); + + NbPrintf(( "ncb_next\n")); + FormattedDump( (PCHAR)&pncbi->u.ncb_next, sizeof( LIST_ENTRY) ); + NbPrintf(( "ncb_iosb\n")); + FormattedDump( (PCHAR)&pncbi->u.ncb_iosb, sizeof( IO_STATUS_BLOCK ) ); + NbPrintf(( "ncb_event %#04x\n", pncbi->ncb_event)); + + if ( (NbDllDebug & NB_DLL_DEBUG_NCB_BUFF) == 0 ) { + NbPrintf(( "\n\n" )); + return; + } + + switch ( pncbi->ncb_command & ~ASYNCH ) { + case NCBSEND: + case NCBCHAINSEND: + case NCBDGSEND: + case NCBSENDNA: + case NCBCHAINSENDNA: + if ( pncbi->ncb_retcode == NRC_PENDING ) { + + // + // If pending then presumably we have not displayed the ncb + // before. After its been sent there isn't much point in displaying + // the buffer again. + // + + NbPrintf(( "ncb_buffer contents:\n")); + FormattedDump( pncbi->ncb_buffer, pncbi->ncb_length ); + } + break; + + case NCBRECV: + case NCBRECVANY: + case NCBDGRECV: + case NCBDGSENDBC: + case NCBDGRECVBC: + case NCBENUM: + case NCBASTAT: + case NCBSSTAT: + case NCBFINDNAME: + if ( pncbi->ncb_retcode != NRC_PENDING ) { + // Buffer has been loaded with data + NbPrintf(( "ncb_buffer contents:\n")); + FormattedDump( pncbi->ncb_buffer, pncbi->ncb_length ); + } + break; + + case NCBCANCEL: + // Buffer has been loaded with the NCB to be cancelled + NbPrintf(( "ncb_buffer contents:\n")); + FormattedDump( pncbi->ncb_buffer, sizeof(NCB)); + break; + } + NbPrintf(( "\n\n" )); +} + +VOID +NbPrint( + char *Format, + ... + ) +/*++ + +Routine Description: + + This routine is equivalent to printf with the output being directed to + stdout. + +Arguments: + + IN char *Format - Supplies string to be output and describes following + (optional) parameters. + +Return Value: + + none. + +--*/ +{ + va_list arglist; + char OutputBuffer[1024]; + ULONG length; + + if ( NbDllDebug == 0 ) { + return; + } + + + va_start( arglist, Format ); + + vsprintf( OutputBuffer, Format, arglist ); + + va_end( arglist ); + + if ( UseConsole ) { + DbgPrint( "%s", OutputBuffer ); + } else { + length = strlen( OutputBuffer ); + if ( LogFile == INVALID_HANDLE_VALUE ) { + if ( UseLogFile ) { + LogFile = CreateFile( LOGNAME, + GENERIC_WRITE, + FILE_SHARE_WRITE, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL ); + if ( LogFile == INVALID_HANDLE_VALUE ) { + // Could not access logfile so use stdout instead + UseLogFile = FALSE; + LogFile = GetStdHandle(STD_OUTPUT_HANDLE); + } + } else { + // Use the applications stdout file. + LogFile = GetStdHandle(STD_OUTPUT_HANDLE); + } + } + + WriteFile( LogFile , (LPVOID )OutputBuffer, length, &length, NULL ); + } + +} // NbPrint + +void +FormattedDump( + PCHAR far_p, + LONG len + ) +/*++ + +Routine Description: + + This routine outputs a buffer in lines of text containing hex and + printable characters. + +Arguments: + + IN far_p - Supplies buffer to be displayed. + IN len - Supplies the length of the buffer in bytes. + +Return Value: + + none. + +--*/ +{ + ULONG l; + char s[80], t[80]; + + if ( len > NbMaxDump ) { + len = NbMaxDump; + } + + while (len) { + l = len < 16 ? len : 16; + + NbPrintf (("%lx ", far_p)); + HexDumpLine (far_p, l, s, t); + NbPrintf (("%s%.*s%s\n", s, 1 + ((16 - l) * 3), "", t)); + + len -= l; + far_p += l; + } +} + +VOID +HexDumpLine( + PCHAR pch, + ULONG len, + PCHAR s, + PCHAR t + ) +/*++ + +Routine Description: + + This routine builds a line of text containing hex and printable characters. + +Arguments: + + IN pch - Supplies buffer to be displayed. + IN len - Supplies the length of the buffer in bytes. + IN s - Supplies the start of the buffer to be loaded with the string + of hex characters. + IN t - Supplies the start of the buffer to be loaded with the string + of printable ascii characters. + + +Return Value: + + none. + +--*/ +{ + static UCHAR rghex[] = "0123456789ABCDEF"; + + UCHAR c; + UCHAR *hex, *asc; + + + hex = s; + asc = t; + + *(asc++) = '*'; + while (len--) { + c = *(pch++); + *(hex++) = rghex [c >> 4] ; + *(hex++) = rghex [c & 0x0F]; + *(hex++) = ' '; + *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c; + } + *(asc++) = '*'; + *asc = 0; + *hex = 0; + +} + +#endif + diff --git a/private/net/netbios/makefile b/private/net/netbios/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/net/netbios/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/net/netbios/netb.h b/private/net/netbios/netb.h new file mode 100644 index 000000000..712be4603 --- /dev/null +++ b/private/net/netbios/netb.h @@ -0,0 +1,134 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + netbios.h + +Abstract: + + This is the main include file for the component of netbios that runs + in the user process. + +Author: + + Colin Watson (ColinW) 24-Jun-91 + +Revision History: + +--*/ + +#include +#include +#include +#include +#include +#include +#include + +// +// Internal version of the ncb layout that uses the reserved area to hold +// the list entry when passing ncb's to the worker thread and the IO status +// block used when the ncb is passed to the netbios device driver. +// + +#include + +// +// Use packing to ensure that the cu union is not forced to word alignment. +// All elements of this structure are naturally aligned. +// + +typedef struct _NCBI { + UCHAR ncb_command; /* command code */ + volatile UCHAR ncb_retcode; /* return code */ + UCHAR ncb_lsn; /* local session number */ + UCHAR ncb_num; /* number of our network name */ + PUCHAR ncb_buffer; /* address of message buffer */ + WORD ncb_length; /* size of message buffer */ + union { + UCHAR ncb_callname[NCBNAMSZ];/* blank-padded name of remote */ + struct _CHAIN_SEND { + WORD ncb_length2; + PUCHAR ncb_buffer2; + } ncb_chain; + } cu; + UCHAR ncb_name[NCBNAMSZ]; /* our blank-padded netname */ + UCHAR ncb_rto; /* rcv timeout/retry count */ + UCHAR ncb_sto; /* send timeout/sys timeout */ + void (CALLBACK *ncb_post)( struct _NCB * ); + /* POST routine address */ + UCHAR ncb_lana_num; /* lana (adapter) number */ + volatile UCHAR ncb_cmd_cplt; /* 0xff => commmand pending */ + + // Make driver specific use of the reserved area of the NCB. + WORD ncb_reserved; /* return to natural alignment */ + union { + LIST_ENTRY ncb_next; /* queued to worker thread */ + IO_STATUS_BLOCK ncb_iosb; /* used for Nt I/O interface */ + } u; + + HANDLE ncb_event; /* HANDLE to Win32 event */ + } NCBI, *PNCBI; + +#include + +VOID +QueueToWorker( + IN PNCBI pncb + ); + +DWORD +Worker( + IN LPVOID Parameter + ); + +VOID +SendNcbToDriver( + IN PNCBI pncb + ); + +VOID +PostRoutineCaller( + PVOID Context, + PIO_STATUS_BLOCK Status, + ULONG Reserved + ); + +VOID +ChainSendPostRoutine( + PVOID Context, + PIO_STATUS_BLOCK Status, + ULONG Reserved + ); + +VOID +HangupConnection( + PNCBI pUserNcb + ); + +#if DBG + +VOID +DisplayNcb( + IN PNCBI pncbi + ); + +#define NbPrintf(String) NbPrint String; + +VOID +NbPrint( + char *Format, + ... + ); + +#else + +// Dispose of debug statements in non-debug builds. +#define DisplayNcb( pncb ) {}; + +#define NbPrintf( String ) {}; + +#endif +// End of Debug related definitions diff --git a/private/net/netbios/netbios.c b/private/net/netbios/netbios.c new file mode 100644 index 000000000..609a90246 --- /dev/null +++ b/private/net/netbios/netbios.c @@ -0,0 +1,1582 @@ +/*++ + +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 +#include +#include + +#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 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; + +VOID +SpinUpAddnameThread( + IN PNCBI pncb + ); + +VOID +AddNameThreadExit( + VOID + ); + +DWORD +SendAddNcbToDriver( + IN PVOID Context + ); + +DWORD +StartNetBIOSDriver( + VOID + ); + + +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. + +--*/ +{ + 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; + + if ( ((ULONG)pncbi & 3) != 0) { + // NCB must be 32 bit aligned + + pncbi->ncb_retcode = pncbi->ncb_cmd_cplt = NRC_BADDR; + return NRC_BADDR; + } + + +#if DBG + // Log when request presented to Netbios + pncbi->ncb_reserved = (WORD)(GetTickCount() / 1000); +#endif + + // 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; + + NbPrintf(( "The Netbios service is starting...\n" )); + + RtlInitUnicodeString( &unicode, NB_DEVICE_NAME); + + ntstatus = StartNB( &objattr, &unicode, &iosb ); + + if (! NT_SUCCESS(ntstatus)) { + // Load the driver + +#if 0 + + NTSTATUS Status = 0; + ULONG Privileges = SE_LOAD_DRIVER_PRIVILEGE; + UNICODE_STRING NBunicode; + + NbPrintf(( "The Netbios service start failed: %X\n", ntstatus )); + + Status = NetpGetPrivilege(1, &Privileges); + if ( NT_SUCCESS( Status )) + { + RtlInitUnicodeString( &NBunicode, NB_REGISTRY_STRING ); + Status = NtLoadDriver( &NBunicode ); + NetpReleasePrivilege(); + } +#endif + DWORD err = 0; + + err = StartNetBIOSDriver(); + + if ( err ) + { + pncbi->ncb_retcode = NRC_OPENERR; + pncbi->ncb_cmd_cplt = NRC_OPENERR; + NbPrintf(( "Netbios returning %lx\n", pncbi->ncb_cmd_cplt )); + LeaveCriticalSection( &Crit ); + return pncbi->ncb_cmd_cplt; + } + else + { + ntstatus = StartNB( &objattr, & unicode, & iosb ); + } + } + + ntstatus = NtCreateEvent( &ReservedEvent, + EVENT_ALL_ACCESS, + NULL, + SynchronizationEvent, + FALSE ); + + if ( !NT_SUCCESS(ntstatus) ) { + NbPrintf(( "The Netbios service start failed: %X\n", ntstatus )); + pncbi->ncb_retcode = NRC_OPENERR; + pncbi->ncb_cmd_cplt = NRC_OPENERR; + NbPrintf(( "Netbios returning %lx\n", pncbi->ncb_cmd_cplt )); + LeaveCriticalSection( &Crit ); + return pncbi->ncb_cmd_cplt; + } + + EventUse = 1; + + Initialized = TRUE; + } + + LeaveCriticalSection( &Crit ); + + } else { + + NbPrintf(( "The Netbios service is already started\n" )); + + } + + // + // Should we use this thread to make the request? + // Once we have a worker thread then requests must pass through it to + // maintain the ordering of requests. + // + // If the caller is using an ASYNCH request then we must use the worker + // thread because Win32 applications don't wait allertable ( user + // APC routines are only called when the thread is allertable). + // + // If the caller only uses ASYNCH=0 requests then the dll will wait + // allertable in the users thread while the operation completes. This + // allows the dll's post routines to fire. + // + + if (( WorkerHandle != NULL ) || + (( pncbi->ncb_command & ASYNCH) == ASYNCH) ) { + + // + // 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(( "Netbios returning %lx\n", pncbi->ncb_cmd_cplt )); + return pncbi->ncb_cmd_cplt; + } + + if ( WorkerHandle == NULL ) { + + HANDLE Threadid; + NTSTATUS Status; + BOOL Flag; + + // Make sure two threads don't simultaneously create worker thread + + EnterCriticalSection( &Crit ); + + if ( WorkerHandle == NULL ) { + + // Initialize shared datastructures + + InitializeListHead( &WorkQueue ); + + Status = NtCreateEvent( &Event, + EVENT_ALL_ACCESS, + NULL, + SynchronizationEvent, + FALSE ); + + if ( !NT_SUCCESS(Status) ) { + pncbi->ncb_retcode = NRC_SYSTEM; + pncbi->ncb_cmd_cplt = NRC_SYSTEM; + NbPrintf(( "Netbios returning %lx\n", pncbi->ncb_cmd_cplt )); + LeaveCriticalSection( &Crit ); + return pncbi->ncb_cmd_cplt; + } + + Status = NtCreateEvent( &AddNameEvent, + EVENT_ALL_ACCESS, + NULL, + NotificationEvent, + FALSE ); + + if ( !NT_SUCCESS(Status) ) { + pncbi->ncb_retcode = NRC_SYSTEM; + pncbi->ncb_cmd_cplt = NRC_SYSTEM; + NbPrintf(( "Netbios returning %lx\n", pncbi->ncb_cmd_cplt )); + NtClose( Event ); + LeaveCriticalSection( &Crit ); + return pncbi->ncb_cmd_cplt; + } + + // All initialization complete -- start worker thread. + + WorkerHandle = CreateThread( + NULL, // Standard thread attributes + 0, // Use same size stack as users + // application + Worker, + // Routine to start in new thread + 0, // Parameter to thread + 0, // No special CreateFlags + (LPDWORD)&Threadid); + + if ( WorkerHandle == NULL ) { + // Generate the best error we can... + pncbi->ncb_retcode = NRC_SYSTEM; + pncbi->ncb_cmd_cplt = NRC_SYSTEM; + NbPrintf(( "Netbios returning %lx\n", pncbi->ncb_cmd_cplt )); + LeaveCriticalSection( &Crit ); + return pncbi->ncb_cmd_cplt; + } + + Flag = SetThreadPriority( + WorkerHandle, + THREAD_PRIORITY_ABOVE_NORMAL ); + + ASSERT( Flag == TRUE ); + if ( Flag != TRUE ) { + NbPrintf(( "Worker SetThreadPriority: %lx\n", GetLastError() )); + } + NbPrintf(( "Worker handle: %lx, threadid %lx\n", Worker, Threadid )); + + AddNameThreadCount = 0; + } + + LeaveCriticalSection( &Crit ); + + } + + if ( (pncb->ncb_command & ASYNCH) == 0 ) { + NTSTATUS Status; + LONG EventOwned; + + // + // 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; + return NRC_SYSTEM; + } + } + + QueueToWorker( pncbi ); + + // + // We must always wait to allow the Apc to fire + // + + 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(( "The Netbios NtWaitForSingleObject failed: %X\n", ntstatus )); + pncbi->ncb_retcode = NRC_SYSTEM; + pncbi->ncb_cmd_cplt = NRC_SYSTEM; + } + + if ( EventOwned == 0) { + InterlockedIncrement( &EventUse ); + } else { + NtClose( pncbi->ncb_event ); + } + + } else { + + QueueToWorker( pncbi ); + + } + + } else { + + // + // Since we are not using the highly compliant callback interface + // we can submit the request using the callers thread. If the request + // is synchronous we do not even look at the callers event. + // + + LONG EventOwned; + NTSTATUS Status; + + ASSERT( (pncbi->ncb_command & ASYNCH) == 0 ); + // + // 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; + NtResetEvent( ReservedEvent, NULL ); + } 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; + return NRC_SYSTEM; + } + } + + pncbi->ncb_post = NULL; // Since ASYNCH not set, post is undefined. + + SendNcbToDriver( pncbi ); + + // + // We must always wait to allow the Apc to fire + // + + 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(( "The Netbios NtWaitForSingleObject failed: %X\n", ntstatus )); + pncbi->ncb_retcode = NRC_SYSTEM; + pncbi->ncb_cmd_cplt = NRC_SYSTEM; + } + + if ( EventOwned == 0) { + InterlockedIncrement( &EventUse ); + } else { + NtClose( pncbi->ncb_event ); + } + } + + NbPrintf(( "NCB being returned: %lx ncb_cmd_cplt: %lx\n", pncbi, pncbi->ncb_cmd_cplt )); + switch ( pncb->ncb_command & ~ASYNCH ) { + case NCBRECV: + case NCBRECVANY: + case NCBDGRECV: + case NCBDGSENDBC: + case NCBDGRECVBC: + case NCBENUM: + case NCBASTAT: + case NCBSSTAT: + case NCBCANCEL: + DisplayNcb( pncbi ); + } + + if ( pncbi->ncb_cmd_cplt == NRC_PENDING ) { + 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); + 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 ); + + NbPrintf(( "Application thread critical\n")); + + InsertTailList( &WorkQueue, &pncb->u.ncb_next ); + + LeaveCriticalSection( &Crit ); + + NbPrintf(( "Application thread not critical %X\n")); + + // Make sure the worker is awake to perform the request + NtSetEvent(Event, NULL); +} + + DWORD +Worker( + 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. + +--*/ +{ + NbPrintf(( "Worker thread started\n" )); + + while ( TRUE) { + NTSTATUS Status; + + // + // Wait for a request to be placed onto the work queue. + // + + // Must wait alertable so that the Apc (post) routine is called. + + NbPrintf(( "Worker thread going to sleep\n" )); + Status = NtWaitForSingleObject( Event, TRUE, NULL ); + NbPrintf(( "Worker thread awake, %X\n", Status)); + + EnterCriticalSection( &Crit ); + + NbPrintf(( "Worker thread critical\n")); + + while (!IsListEmpty(&WorkQueue)) { + + PLIST_ENTRY entry; + PNCBI pncb; + + entry = RemoveHeadList(&WorkQueue); + + LeaveCriticalSection( &Crit ); + + NbPrintf(( "Worker thread not critical\n")); + + // Zero out reserved field again + entry->Flink = entry->Blink = 0; + + pncb = CONTAINING_RECORD( entry, NCBI, u.ncb_next ); + + // Give ncb to the driver specifying the callers APC routine + + NbPrintf(( "Worker thread processing ncb: %lx\n", pncb)); + + 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 ); + } + + NbPrintf(( "Worker thread submitted ncb: %lx\n", pncb)); + + EnterCriticalSection( &Crit ); + + NbPrintf(( "Worker thread critical\n")); + + } + + LeaveCriticalSection( &Crit ); + NbPrintf(( "Worker thread not critical\n")); + + } + + return 0; + + UNREFERENCED_PARAMETER( Parameter ); +} + + + + 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(( "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(( "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(( "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(( "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(( "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(( "PNCB: %lx completed, status:%lx, ncb_retcode: %#04x\n", + pncb, + ntstatus, + pncb->ncb_retcode )); + + return; + } + + default: + buffer = NULL; + length = 0; + break; + } + + NbPrintf(( "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(( "The Netbios NtDeviceIoControlFile failed: %X\n", ntstatus )); + + if ( ntstatus == STATUS_ACCESS_VIOLATION ) { + pncb->ncb_retcode = NRC_BUFLEN; + } else { + pncb->ncb_retcode = NRC_SYSTEM; + } + PostRoutineCaller( pncb, &pncb->u.ncb_iosb, 0); + } + + NbPrintf(( "PNCB: %lx completed, status:%lx, ncb_retcode: %#04x\n", + pncb, + ntstatus, + pncb->ncb_retcode )); + + 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; + + NbPrintf(( "Worker thread create addname thread\n" )); + + 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(( "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(( "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; + } + + NbPrintf(( "Addname/Astat Worker thread submitting %x\n", pncb)); + + 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(( "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); + } + + NbPrintf(( "Addname/Astat Worker thread returning %x, %x\n", pncb, pncb->ncb_retcode)); + + 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(( "Netbios: Access Violation post processing NCB %lx\n", pncb )); + NbPrintf(( "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 { + + NbPrintf(( "PostRoutineCaller 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; + + // 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(( "Netbios: Access Violation post processing NCB %lx\n", pncbi )); + NbPrintf(( "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(( "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; + + // 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(( "Netbios: Access Violation post processing NCB %lx\n", pncbi )); + NbPrintf(( "Netbios: Probable application error\n" )); + } + + NbPrintf(( "BigBuffer Free: %lx\n", BigBuffer)); + 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(( "Hangup Session PNCBI: %lx failed to create event!\n" )); + return; + } + + NbPrintf(( "Hangup Session PNCBI: %lx\n", pUserNcb )); + + 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(( "The Netbios NtWaitForSingleObject failed: %X\n", Status )); + } + + NtClose( ncbi.ncb_event ); + + NbPrintf(( "Hangup Session complete PNCBI: %lx\n", pUserNcb )); +} + + VOID +NetbiosInitialize( + VOID + ) +/*++ + +Routine Description: + + This routine is called each time a process that uses netapi.dll + starts up. + +Arguments: + + none. + +Return Value: + + none. + +--*/ +{ + + Initialized = FALSE; + WorkerHandle = NULL; + InitializeCriticalSection( &Crit ); + +} + + 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. + +--*/ +{ + DeleteCriticalSection( &Crit ); + if ( Initialized == FALSE ) { + // This process did not use Netbios. + return; + } + + NtClose(NB); +} diff --git a/private/net/netbios/netbios.rc b/private/net/netbios/netbios.rc new file mode 100644 index 000000000..2adf8b84e --- /dev/null +++ b/private/net/netbios/netbios.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NetBIOS Interface Library" +#define VER_INTERNALNAME_STR "netbios.dll" + +#include "common.ver" + diff --git a/private/net/netbios/sources b/private/net/netbios/sources new file mode 100644 index 000000000..cc81165f6 --- /dev/null +++ b/private/net/netbios/sources @@ -0,0 +1,62 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=net +MINORCOMP=netbios + + +NTPROFILEINPUT=YES + +TARGETNAME=netbios +TARGETPATH=obj +TARGETTYPE=LIBRARY + +INCLUDES=..\inc;..\..\inc +UMLIBS=\nt\public\sdk\lib\*\netapi32.lib + +!IFNDEF DISABLE_NET_UNICODE +UNICODE=1 +NET_C_DEFINES=-DUNICODE +!ENDIF + +SOURCES= \ + debug.c \ + netbios.c \ + netbios.rc + +USE_CL860_LARGE_MODEL=1 +UMTYPE=console +UMTEST=tnetbios*tnetbl*tnetcall*tnbf*tdgs*tdgr*tbdgs*tbdgr*tchain*thang + +!IFNDEF 386_WARNING_LEVEL +386_WARNING_LEVEL=/W3 +!ENDIF +!IFNDEF 860_WARNING_LEVEL +860_WARNING_LEVEL=-W3 +!ENDIF +!IFNDEF SIM_WARNING_LEVEL +SIM_WARNING_LEVEL=-W3 +!ENDIF + + diff --git a/private/net/netbios/tnetbios.c b/private/net/netbios/tnetbios.c new file mode 100644 index 000000000..ff98fd272 --- /dev/null +++ b/private/net/netbios/tnetbios.c @@ -0,0 +1,343 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + tnetbios.c + +Abstract: + + This module contains code which exercises the NetBIOS dll and driver. + +Author: + + Colin Watson (ColinW) 13-Mar-1991 + +Environment: + + Application mode + +Revision History: + + Dave Beaver (DBeaver) 10 August 1991 + + Modify to support multiple LAN numbers + + Jerome Nantel (w-jeromn) 23 August 1991 + + Add Event Signaling testing + +--*/ + +#include +#include +#include +#define WIN32_CONSOLE_APP +#include + +#include +#include + +// 1234567890123456 +#define SPACES " " +#define TIMEOUT 60000 // Time out for wait, set at 1 minute +#define Hi "Come here Dave, I need you" + +#define ClearNcb( PNCB ) { \ + RtlZeroMemory( PNCB , sizeof (NCB) ); \ + RtlMoveMemory( (PNCB)->ncb_name, SPACES, sizeof(SPACES)-1 );\ + RtlMoveMemory( (PNCB)->ncb_callname, SPACES, sizeof(SPACES)-1 );\ + } + +// Hard code lana-num that is mapped to XNS + +int Limit = 20; + +VOID +usage ( + VOID + ) +{ + printf("usage: tnetbios [-n:lan number][-h][-e] \n"); + printf(" -n specifies the lan number (0 is the default)\n"); + printf(" -h specifies that addresses are hexadecimal numbers \n"); + printf(" rather than strings.\n"); + printf(" -e test event signaling for ASYNC calls. \n"); + printf(" final two arguments are the remote and local computer names.\n"); +} + +int +main (argc, argv) + int argc; + char *argv[]; +{ + NCB myncb; + CHAR Buffer[128]; + CHAR Buffer2[128]; + int i,j; + CHAR localName[16]; + CHAR remoteName[16]; + CHAR localTemp[32]; + CHAR remoteTemp[32]; + ULONG lanNumber=0; + BOOLEAN gotFirst=FALSE; + BOOLEAN asHex=FALSE; + BOOLEAN tevent=FALSE; + UCHAR lsn; + UCHAR name_number; + HANDLE event; + + if ( argc < 3 || argc > 6) { + usage (); + return 1; + } + + // + // dbeaver: added switch to allow 32 byte hex string as name to facilitate + // testing under unusual circumstances + // + + for (j=1;j<16;j++ ) { + localTemp[j] = ' '; + remoteTemp[j] = ' '; + } + + // + // parse the switches + // + + for (i=1;i +#include +#include +#include + +#include +#include + +// 1234567890123456 +#define SPACES " " + +#define Hi "Come here Dave, I need you" + +#define ClearNcb( PNCB ) { \ + RtlZeroMemory( PNCB , sizeof (NCB) ); \ + RtlMoveMemory( (PNCB)->ncb_name, SPACES, sizeof(SPACES)-1 );\ + RtlMoveMemory( (PNCB)->ncb_callname, SPACES, sizeof(SPACES)-1 );\ + } + +// Hard code lana-num that is mapped to XNS + +#define XNS 1 +int Limit = 20; + +VOID +usage ( + VOID + ) +{ + printf("usage: tnetbl [-n:lan number][-h] \n"); + printf(" -n specifies the lan number (0 is the default)\n"); + printf(" -h specifies that addresses are hexadecimal numbers \n"); + printf(" rather than strings.\n"); + printf(" final two arguments are the remote and local computer names.\n"); +} + +int +main (argc, argv) + int argc; + char *argv[]; +{ + NCB myncb; + CHAR Buffer2[128]; + int i,j; + CHAR localName[16]; + CHAR remoteName[16]; + CHAR localTemp[32]; + CHAR remoteTemp[32]; + ULONG lanNumber=0; + BOOLEAN gotFirst=FALSE; + BOOLEAN asHex=FALSE; + UCHAR lsn; + UCHAR name_number; + USHORT length; + + if ( argc < 3 || argc > 5) { + usage (); + return 1; + } + + // + // dbeaver: added switch to allow 32 byte hex string as name to facilitate + // testing under unusual circumstances + // + + for (j=1;j<16;j++ ) { + localTemp[j] = ' '; + remoteTemp[j] = ' '; + } + + // + // parse the switches + // + + for (i=1;i +#include +#include +#define WIN32_CONSOLE_APP +#include + +#include +#include + +// 1234567890123456 +#define SPACES " " +#define TIMEOUT 60000 // Time out for wait, set at 1 minute +#define Hi "Come here Dave, I need you" +#define SEND 1 +#define RCV 0 + + +NCB myncb[2]; +CHAR Buffer[16384+1024]; +CHAR Buffer2[16384+1024]; +ULONG lanNumber=0; +UCHAR lsn; +HANDLE twoEvent[2]; +int count; // frame count +BOOLEAN verbose=FALSE; +BOOLEAN rxany=FALSE; +BOOLEAN rxanyany=FALSE; +BOOLEAN input=TRUE; +BOOLEAN output=TRUE; +int QuietCount = 50; +UCHAR name_number; + +VOID +usage ( + VOID + ) +{ + printf("usage: tsrnetb -c|l [-[a|r]] [-[i|o]] [-n:lan number][-h] \n"); + printf(" -c specifies calling, -l specifies listener\n"); + printf(" -a specifies rx any, any, -r specifies rx any\n"); + printf(" -i specifies rx only, -o specifies tx only\n"); + printf(" -d specifies delay with alerts on each tx/rx\n"); + printf(" -n specifies the lan number (0 is the default)\n"); + printf(" -h specifies that addresses are hexadecimal numbers \n"); + printf(" rather than strings.\n"); + printf(" -g use group name for the connection\n"); + printf(" -v verbose\n"); + printf(" -s silent\n"); + printf(" -t token ring, lan status alert (names ignored)\n"); + printf(" -q quiet (print r every 50 receives\n"); + printf(" final two arguments are the remote and local computer names.\n"); +} + +VOID +ClearNcb( PNCB pncb ) { + RtlZeroMemory( pncb , sizeof (NCB) ); + RtlMoveMemory( pncb->ncb_name, SPACES, sizeof(SPACES)-1 ); + RtlMoveMemory( pncb->ncb_callname, SPACES, sizeof(SPACES)-1 ); +} + +VOID StartSend() +{ + + ClearNcb( &(myncb[0]) ); + if ( output == FALSE ) { + ResetEvent(twoEvent[SEND]); + return; + } + myncb[0].ncb_command = NCBSEND | ASYNCH; + myncb[0].ncb_lana_num = (UCHAR)lanNumber; + myncb[0].ncb_buffer = Buffer; + myncb[0].ncb_lsn = lsn; + myncb[0].ncb_event = twoEvent[SEND]; + RtlMoveMemory( Buffer, Hi, sizeof( Hi )); + sprintf( Buffer, "%s %d\n", Hi, count ); + if ( verbose == TRUE ) { + printf( "Tx: %s", Buffer ); + } + count++; + myncb[0].ncb_length = (WORD)sizeof(Buffer); + Netbios( &(myncb[0]) ); + +} + +VOID StartRcv() +{ + ClearNcb( &(myncb[1]) ); + if ( input == FALSE ) { + ResetEvent(twoEvent[RCV]); + return; + } + if ((rxany == FALSE) && + (rxanyany == FALSE)) { + myncb[1].ncb_command = NCBRECV | ASYNCH; + } else { + myncb[1].ncb_command = NCBRECVANY | ASYNCH; + } + myncb[1].ncb_lana_num = (UCHAR)lanNumber; + myncb[1].ncb_length = sizeof( Buffer2 ); + myncb[1].ncb_buffer = Buffer2; + if ( rxany == FALSE ) { + if ( rxanyany == FALSE ) { + myncb[1].ncb_lsn = lsn; + } else { + myncb[1].ncb_num = 0xff; + } + } else{ + myncb[1].ncb_num = name_number; + } + myncb[1].ncb_lsn = lsn; + myncb[1].ncb_event = twoEvent[RCV]; + Netbios( &(myncb[1]) ); +} + +int +_cdecl +main (argc, argv) + int argc; + char *argv[]; +{ + + int i,j; + int rcvCount=0; + CHAR localName[17]; + CHAR remoteName[17]; + CHAR localTemp[32]; + CHAR remoteTemp[32]; + BOOLEAN gotFirst=FALSE; + BOOLEAN asHex=FALSE; + BOOLEAN listen=FALSE; + BOOLEAN quiet=FALSE; + BOOLEAN delay=FALSE; + BOOLEAN group=FALSE; + BOOLEAN silent=FALSE; + BOOLEAN lanalert=FALSE; + DWORD tevent; + BOOLEAN ttwo=FALSE; + + if ( argc < 4 || argc > 9) { + usage (); + return 1; + } + + // + // dbeaver: added switch to allow 32 byte hex string as name to facilitate + // testing under unusual circumstances + // + + for (j=1;j<16;j++ ) { + localTemp[j] = ' '; + remoteTemp[j] = ' '; + } + + // + // parse the switches + // + + for (i=1;i