summaryrefslogtreecommitdiffstats
path: root/private/net/netbios
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/net/netbios
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/net/netbios')
-rw-r--r--private/net/netbios/debug.c369
-rw-r--r--private/net/netbios/makefile6
-rw-r--r--private/net/netbios/netb.h134
-rw-r--r--private/net/netbios/netbios.c1582
-rw-r--r--private/net/netbios/netbios.rc11
-rw-r--r--private/net/netbios/sources62
-rw-r--r--private/net/netbios/tnetbios.c343
-rw-r--r--private/net/netbios/tnetbl.c269
-rw-r--r--private/net/netbios/tnetcall.c491
9 files changed, 3267 insertions, 0 deletions
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 <netb.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+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 <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <nb30.h>
+#include <nb30p.h>
+#include <netbios.h>
+
+//
+// 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 <packon.h>
+
+//
+// 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 <packoff.h>
+
+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 <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 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 <windows.h>
+
+#include <ntverp.h>
+
+#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 <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#define WIN32_CONSOLE_APP
+#include <windows.h>
+
+#include <nb30.h>
+#include <stdio.h>
+
+// 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] <remote computername> <my computername>\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<argc ;i++ ) {
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'n':
+ if (!NT_SUCCESS(RtlCharToInteger (&argv[i][3], 10, &lanNumber))) {
+ usage ();
+ return 1;
+ }
+ break;
+
+ case 'h':
+ asHex = TRUE;
+ break;
+ case 'e':
+ tevent = TRUE;
+ break;
+ default:
+ usage ();
+ return 1;
+ break;
+
+ }
+
+ } else {
+
+ //
+ // not a switch must be a name
+ //
+
+ if (gotFirst != TRUE) {
+ RtlMoveMemory (remoteTemp, argv[i], lstrlen( argv[i] ));
+ gotFirst = TRUE;
+ } else {
+ RtlMoveMemory (localTemp, argv[i], lstrlen( argv[i] ));
+ }
+
+ }
+ }
+
+ if (asHex) {
+ RtlZeroMemory (localName, 16);
+ RtlZeroMemory (remoteName, 16);
+
+ for (j=0;j<16 ;j+=4) {
+ RtlCharToInteger (&localTemp[j*2], 16, (PULONG)&localName[j]);
+ }
+
+ for (j=0;j<16 ;j+=4) {
+ RtlCharToInteger (&remoteTemp[j*2], 16, (PULONG)&remoteName[j]);
+ }
+
+ } else {
+ for (j=1;j<16;j++ ) {
+ localName[j] = ' ';
+ remoteName[j] = ' ';
+ }
+
+ RtlMoveMemory( localName, localTemp, 16);
+ RtlMoveMemory( remoteName, remoteTemp, 16);
+ }
+
+ if (tevent) {
+ /* Testing event signaling to handle async calls */
+ if (( event = CreateEvent( NULL, FALSE, FALSE, NULL )) == NULL ) {
+ /* Could not get event handle. Abort */
+ printf("Could not test event signaling.\n");
+ tevent=FALSE;
+ }
+ }
+
+ printf( "Starting NetBios\n" );
+ {
+ LANA_ENUM Enum;
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBENUM;
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_length = sizeof(Enum);
+ myncb.ncb_buffer = &Enum;
+ Netbios( &myncb );
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ printf( " Enum Failed %x\n", myncb.ncb_retcode );
+ return 1;
+ }
+ }
+
+ // Reset
+
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBRESET;
+ myncb.ncb_lsn = 0; // Request resources
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_callname[0] = 0; // 16 sessions
+ myncb.ncb_callname[1] = 0; // 16 commands
+ myncb.ncb_callname[2] = 0; // 8 names
+ Netbios( &myncb );
+
+ // Add name
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBADDNAME;
+ RtlMoveMemory( myncb.ncb_name, localName, lstrlen(localName));
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ Netbios( &myncb );
+
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ printf( " Addname Failed %x\n", myncb.ncb_retcode );
+ return 1;
+ }
+ name_number = myncb.ncb_num;
+
+ {
+ struct {
+ ADAPTER_STATUS AdapterInfo;
+ NAME_BUFFER Names[16];
+ } AdapterStatus;
+ // Adapter Status
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBASTAT;
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_length = sizeof( AdapterStatus );
+ myncb.ncb_buffer = (CHAR *)&AdapterStatus;
+ Netbios( &myncb );
+ }
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ printf( " Adapter Status Failed %x\n", myncb.ncb_retcode );
+ return 1;
+ }
+
+ for ( j = 0; j <= Limit; j++ ) {
+
+ // Call
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBCALL | ASYNCH;
+ RtlMoveMemory( myncb.ncb_name, localName, lstrlen(localName ));
+ RtlMoveMemory( myncb.ncb_callname, remoteName, lstrlen( remoteName ));
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_rto = myncb.ncb_rto = 0;//10; 10*500 milliseconds timeout
+ myncb.ncb_num = name_number;
+ if ( tevent ) myncb.ncb_event = event;
+ while ( TRUE) {
+ printf("\nStart NCB CALL ");
+ Netbios( &myncb );
+ printf( " Call returned " );
+ if ( tevent ) {
+ if ( WaitForSingleObject( event, TIMEOUT ) ) {
+ // Wait timed out, no return
+ printf("ERROR: Wait timed out, event not signaled.\n");
+ }
+ } else {
+ while ( myncb.ncb_cmd_cplt == NRC_PENDING ) {
+ printf( "." );
+ Sleep(500);
+
+ }
+ }
+ lsn = myncb.ncb_lsn;
+
+ if ( myncb.ncb_retcode == NRC_GOODRET ) {
+ // Success
+ printf( " Call completed\n" );
+ break;
+ }
+ printf( " Call completed with error %lx, retry\n", myncb.ncb_retcode );
+ }
+
+ for ( i = 0; i <=j ; i++ ) {
+
+ // Send
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBSEND;
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_length = sizeof( Hi );
+ myncb.ncb_buffer = Buffer;
+ myncb.ncb_lsn = lsn;
+ RtlMoveMemory( Buffer, Hi, sizeof( Hi ));
+ Netbios( &myncb );
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ break;
+ }
+
+ // Receive
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBRECV | ASYNCH;
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_length = sizeof( Buffer2 );
+ myncb.ncb_buffer = Buffer2;
+ myncb.ncb_lsn = lsn;
+ if ( tevent ) myncb.ncb_event = event;
+ Netbios( &myncb );
+ printf( "R" );
+ if ( tevent ) {
+ if ( WaitForSingleObject( event, TIMEOUT ) ) {
+ // Wait timed out, no return
+ printf("ERROR: Wait timed out, event not signaled.\n");
+ }
+ } else {
+ while ( myncb.ncb_cmd_cplt == NRC_PENDING ) {
+ printf( "." );
+ Sleep(500);
+
+ }
+ }
+ printf( "r" );
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ break;
+ }
+ // printf( ":%s\n", Buffer2);
+ }
+
+ // Hangup
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBHANGUP;
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_lsn = lsn;
+ Netbios( &myncb );
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ break;
+ }
+ }
+
+ // Reset
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBRESET;
+ myncb.ncb_lsn = 1; // Free resources
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ Netbios( &myncb );
+ printf( "Ending NetBios\n" );
+
+ if (tevent ) {
+ // Close handle
+ CloseHandle( event );
+ }
+
+ return 0;
+}
diff --git a/private/net/netbios/tnetbl.c b/private/net/netbios/tnetbl.c
new file mode 100644
index 000000000..268877208
--- /dev/null
+++ b/private/net/netbios/tnetbl.c
@@ -0,0 +1,269 @@
+/*++
+
+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
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <nb30.h>
+#include <stdio.h>
+
+// 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] <remote computername> <my computername>\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<argc ;i++ ) {
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'n':
+ if (!NT_SUCCESS(RtlCharToInteger (&argv[i][3], 10, &lanNumber))) {
+ usage ();
+ return 1;
+ }
+ break;
+
+ case 'h':
+ asHex = TRUE;
+ break;
+ default:
+ usage ();
+ return 1;
+ break;
+
+ }
+
+ } else {
+
+ //
+ // not a switch must be a name
+ //
+
+ if (gotFirst != TRUE) {
+ RtlMoveMemory (remoteTemp, argv[i], lstrlen( argv[i] ));
+ gotFirst = TRUE;
+ } else {
+ RtlMoveMemory (localTemp, argv[i], lstrlen( argv[i] ));
+ }
+
+ }
+ }
+
+ if (asHex) {
+ RtlZeroMemory (localName, 16);
+ RtlZeroMemory (remoteName, 16);
+
+ for (j=0;j<16 ;j+=4) {
+ RtlCharToInteger (&localTemp[j*2], 16, (PULONG)&localName[j]);
+ }
+
+ for (j=0;j<16 ;j+=4) {
+ RtlCharToInteger (&remoteTemp[j*2], 16, (PULONG)&remoteName[j]);
+ }
+
+ } else {
+ for (j=1;j<16;j++ ) {
+ localName[j] = ' ';
+ remoteName[j] = ' ';
+ }
+
+ RtlMoveMemory( localName, localTemp, 16);
+ RtlMoveMemory( remoteName, remoteTemp, 16);
+ }
+
+ // Reset
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBRESET;
+ myncb.ncb_lsn = 0; // Request resources
+ myncb.ncb_lana_num = lanNumber;
+ myncb.ncb_callname[0] = 0; // 16 sessions
+ myncb.ncb_callname[1] = 0; // 16 commands
+ myncb.ncb_callname[2] = 0; // 8 names
+ Netbios( &myncb );
+
+ // AddName
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBADDNAME;
+ RtlMoveMemory( myncb.ncb_name, localName, 16);
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ Netbios( &myncb );
+
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ printf( "Addname returned an error %lx", myncb.ncb_retcode );
+ return 1;
+ }
+ name_number = myncb.ncb_num;
+
+ printf( "Starting Listen test\n" );
+
+ for ( j = 0; j <= Limit; j++ ) {
+
+ printf( "\nStarting Listen " );
+
+ // Listen
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBLISTEN | ASYNCH;
+ RtlMoveMemory( myncb.ncb_name, localName, 16);
+ RtlMoveMemory( myncb.ncb_callname, remoteName, 16);
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_rto = myncb.ncb_rto = 0; //10; 10*500 milliseconds timeout
+ myncb.ncb_num = name_number;
+ Netbios( &myncb );
+ printf( "Listen returned " );
+ while ( myncb.ncb_cmd_cplt == NRC_PENDING ) {
+ printf( "." );
+ Sleep(500);
+
+ }
+ printf( " Listen completed\n" );
+
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ break;
+ }
+
+ lsn = myncb.ncb_lsn;
+
+ while ( 1 ) {
+
+ // Receive
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBRECV | ASYNCH;
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_length = sizeof( Buffer2 );
+ myncb.ncb_buffer = Buffer2;
+ myncb.ncb_lsn = lsn;
+ Netbios( &myncb );
+ printf( "R" );
+ while ( myncb.ncb_cmd_cplt == NRC_PENDING ) {
+// printf( "." );
+// Sleep(250);
+
+ }
+ printf( "r" );
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ break;
+ }
+ //printf( ":%s\n", Buffer2);
+ length = myncb.ncb_length;
+
+ // Send
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBSEND;
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_length = length;
+ myncb.ncb_buffer = Buffer2;
+ myncb.ncb_lsn = lsn;
+ Netbios( &myncb );
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ break;
+ }
+ }
+
+ // Hangup
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBHANGUP;
+ myncb.ncb_lana_num = (UCHAR)lanNumber;
+ myncb.ncb_lsn = lsn;
+ Netbios( &myncb );
+ if ( myncb.ncb_retcode != NRC_GOODRET ) {
+ break;
+ }
+ }
+
+ // Reset
+ ClearNcb( &myncb );
+ myncb.ncb_command = NCBRESET;
+ myncb.ncb_lsn = 1; // Free resources
+ Netbios( &myncb );
+ printf( "Ending NetBios\n" );
+
+ return 0;
+}
+
diff --git a/private/net/netbios/tnetcall.c b/private/net/netbios/tnetcall.c
new file mode 100644
index 000000000..4e32cbe33
--- /dev/null
+++ b/private/net/netbios/tnetcall.c
@@ -0,0 +1,491 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ tnetcall.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 <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#define WIN32_CONSOLE_APP
+#include <windows.h>
+
+#include <nb30.h>
+#include <stdio.h>
+
+// 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] <remote computername> <my computername>\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<argc ;i++ ) {
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'n':
+ if (!NT_SUCCESS(RtlCharToInteger (&argv[i][3], 10, &lanNumber))) {
+ usage ();
+ return 1;
+ }
+ break;
+
+ case 'h':
+ asHex = TRUE;
+ break;
+ case 'c':
+ listen = FALSE;
+ break;
+ case 'a':
+ rxany = TRUE;
+ break;
+ case 'r':
+ rxanyany = TRUE;
+ break;
+ case 'i':
+ output = FALSE;
+ break;
+ case 'o':
+ input = FALSE;
+ break;
+ case 'd':
+ delay = FALSE;
+ break;
+ case 'l':
+ listen = TRUE;
+ break;
+ case 'q':
+ quiet = TRUE;
+ silent = TRUE;
+ break;
+ case 'g':
+ group = TRUE;
+ break;
+ case 'v':
+ verbose = TRUE;
+ break;
+ case 's':
+ silent = TRUE;
+ break;
+ case 't':
+ lanalert = TRUE;
+ break;
+ default:
+ usage ();
+ return 1;
+ break;
+
+ }
+
+ } else {
+
+ //
+ // not a switch must be a name
+ //
+
+ if (gotFirst != TRUE) {
+ RtlMoveMemory (remoteTemp, argv[i], lstrlenA( argv[i] ));
+ gotFirst = TRUE;
+ } else {
+ RtlMoveMemory (localTemp, argv[i], lstrlenA( argv[i] ));
+ }
+
+ }
+ }
+ if ((rxany == TRUE) &&
+ (rxanyany == TRUE)) {
+ usage();
+ return 1;
+ }
+ if ((input == FALSE) &&
+ (output == FALSE)) {
+ usage();
+ return 1;
+ }
+
+ if (asHex) {
+ RtlZeroMemory (localName, 16);
+ RtlZeroMemory (remoteName, 16);
+
+ for (j=0;j<16 ;j+=4) {
+ RtlCharToInteger (&localTemp[j*2], 16, (PULONG)&localName[j]);
+ }
+
+ for (j=0;j<16 ;j+=4) {
+ RtlCharToInteger (&remoteTemp[j*2], 16, (PULONG)&remoteName[j]);
+ }
+
+ } else {
+ for (j=1;j<16;j++ ) {
+ localName[j] = ' ';
+ remoteName[j] = ' ';
+ }
+
+ RtlMoveMemory( localName, localTemp, 16);
+ RtlMoveMemory( remoteName, remoteTemp, 16);
+ }
+
+ for ( i=0; i<2; i++ ) {
+ if (( twoEvent[i] = CreateEvent( NULL, TRUE, FALSE, NULL )) == NULL ) {
+ /* Could not get event handle. Abort */
+ printf("Could not test event signaling.\n");
+ return 1;
+ }
+ }
+
+ printf( "Starting NetBios\n" );
+
+ // Reset
+ ClearNcb( &(myncb[0]) );
+ myncb[0].ncb_command = NCBRESET;
+ myncb[0].ncb_lsn = 0; // Request resources
+ myncb[0].ncb_lana_num = (UCHAR)lanNumber;
+ myncb[0].ncb_callname[0] = 0; // 16 sessions
+ myncb[0].ncb_callname[1] = 0; // 16 commands
+ myncb[0].ncb_callname[2] = 0; // 8 names
+ myncb[0].ncb_callname[3] = 0; // Don't want the reserved address
+ Netbios( &(myncb[0]) );
+
+ if ( lanalert == TRUE ) {
+ ClearNcb( &(myncb[0]) );
+ myncb[0].ncb_command = NCBLANSTALERT;
+ myncb[0].ncb_lana_num = (UCHAR)lanNumber;
+ Netbios( &(myncb[0]) );
+ if ( myncb[0].ncb_retcode != NRC_GOODRET ) {
+ printf( " LanStatusAlert failed %x", myncb[1].ncb_retcode);
+ }
+ return 0;
+ }
+
+ // Add name
+ ClearNcb( &(myncb[0]) );
+ if ( group == FALSE) {
+ myncb[0].ncb_command = NCBADDNAME;
+ } else {
+ myncb[0].ncb_command = NCBADDGRNAME;
+ }
+ RtlMoveMemory( myncb[0].ncb_name, localName, 16);
+ myncb[0].ncb_lana_num = (UCHAR)lanNumber;
+ Netbios( &(myncb[0]) );
+ name_number = myncb[0].ncb_num;
+
+ if ( listen == FALSE ) {
+ // Call
+ printf( "\nStarting Call " );
+ ClearNcb( &(myncb[0]) );
+ myncb[0].ncb_command = NCBCALL | ASYNCH;
+ RtlMoveMemory( myncb[0].ncb_name, localName, 16);
+ RtlMoveMemory( myncb[0].ncb_callname,remoteName, 16);
+ myncb[0].ncb_lana_num = (UCHAR)lanNumber;
+ myncb[0].ncb_sto = myncb[0].ncb_rto = 120; // 120*500 milliseconds timeout
+ myncb[0].ncb_num = name_number;
+ myncb[0].ncb_event = twoEvent[0];
+ while ( TRUE) {
+ printf("\nStart NCB CALL ");
+ Netbios( &(myncb[0]) );
+ printf( " Call returned " );
+ if ( myncb[0].ncb_cmd_cplt == NRC_PENDING ) {
+ if ( WaitForSingleObject( twoEvent[0], TIMEOUT ) ) {
+ // Wait timed out, no return
+ printf("ERROR: Wait timed out, event not signaled.\n");
+ }
+ }
+ printf( " Call completed\n" );
+ lsn = myncb[0].ncb_lsn;
+
+ if ( myncb[0].ncb_retcode == NRC_GOODRET ) {
+ // Success
+ break;
+ }
+ printf("Call completed with error %lx, retry", myncb[0].ncb_retcode );
+ Sleep(5);
+ }
+ } else {
+ printf( "\nStarting Listen " );
+
+ // Listen
+ ClearNcb( &(myncb[0]) );
+ myncb[0].ncb_command = NCBLISTEN | ASYNCH;
+ RtlMoveMemory( myncb[0].ncb_name, localName, 16);
+ RtlMoveMemory( myncb[0].ncb_callname, remoteName, 16);
+ myncb[0].ncb_lana_num = (UCHAR)lanNumber;
+ myncb[0].ncb_sto = myncb[0].ncb_rto = 120; // 120*500 milliseconds timeout
+ myncb[0].ncb_num = name_number;
+ Netbios( &(myncb[0]) );
+ printf( "Listen returned " );
+ while ( myncb[0].ncb_cmd_cplt == NRC_PENDING ) {
+ printf( "." );
+ Sleep(500);
+
+ }
+ printf( " Listen completed\n" );
+
+ if ( myncb[0].ncb_retcode != NRC_GOODRET ) {
+ printf("ERROR: Could not establish session.\n");
+ return 1;
+ }
+
+ lsn = myncb[0].ncb_lsn;
+
+ }
+
+ count = 0;
+ StartSend();
+ StartRcv();
+
+ while ( TRUE ) {
+
+ tevent = WaitForMultipleObjects(2, twoEvent, FALSE, TIMEOUT);
+
+ switch ( tevent ) {
+ case SEND :
+ // Send completed, start a new one.
+ if ( silent == FALSE ) {
+ printf("S");
+ }
+ if ( myncb[0].ncb_retcode != NRC_GOODRET ) {
+ printf( "Send failed %x", myncb[0].ncb_retcode);
+ goto Cleanup;
+ }
+ if ( delay == TRUE ) {
+ // Wait alertable - useful for debugging APC problems.
+ NtWaitForSingleObject(
+ twoEvent[SEND],
+ TRUE,
+ NULL );
+ }
+
+ StartSend();
+ break;
+
+ case RCV :
+ if ( silent == FALSE ) {
+ printf("R");
+ }
+ if ( (quiet == TRUE) && (QuietCount-- == 0) ) {
+ printf("R");
+ QuietCount = 50;
+ }
+ if ( myncb[1].ncb_retcode != NRC_GOODRET ) {
+ printf( " Receive failed %x", myncb[1].ncb_retcode);
+ goto Cleanup;
+ } else {
+ if ( verbose == TRUE ) {
+ printf( "Rx: %s", Buffer2 );
+ }
+ }
+ // Receive completed, start a new one.
+
+ if ( delay == TRUE ) {
+ // Wait alertable
+ NtWaitForSingleObject(
+ twoEvent[RCV],
+ TRUE,
+ NULL );
+ }
+
+ StartRcv();
+ rcvCount++;
+ break;
+
+ default:
+ printf("WARNING: Wait timed out, no event signaled.\n");
+ break;
+ }
+
+ }
+Cleanup:
+ // Hangup
+ ClearNcb( &(myncb[0]) );
+ myncb[0].ncb_command = NCBHANGUP;
+ myncb[0].ncb_lana_num = (UCHAR)lanNumber;
+ myncb[0].ncb_lsn = lsn;
+ Netbios( &(myncb[0]) );
+ if ( myncb[0].ncb_retcode != NRC_GOODRET ) {
+ printf( " Hangup failed %x", myncb[1].ncb_retcode);
+ }
+
+ // Reset
+ ClearNcb( &(myncb[0]) );
+ myncb[0].ncb_command = NCBRESET;
+ myncb[0].ncb_lsn = 1; // Free resources
+ myncb[0].ncb_lana_num = (UCHAR)lanNumber;
+ Netbios( &(myncb[0]) );
+ printf( "Ending NetBios\n" );
+
+ // Close handles
+ CloseHandle( twoEvent[0] );
+ CloseHandle( twoEvent[1] );
+
+ return 0;
+
+}
+