/*++

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;

}