/*++

Copyright (c) 1993 Microsoft Corporation

Module Name:

    netbcom.c

Abstract:

    NetBios transport common code between server and client.

Author:

    Steven Zeck (stevez) 2/12/92

    Danny Glasser (dannygl) 3/1/93

--*/

#include "NetBCom.h"

// The maximum value of a lana number
#define MAX_LANA_NUMBER     UCHAR_MAX

// This the netbios name of this (self) machine.
// NOTE:  We assume one-byte characters here

unsigned char MachineName[NCBNAMSZ];
size_t MachineNameLengthUnpadded;

CRITICAL_SECTION NetBiosMutex;

PROTOCOL_MAP ProtoToLana[MAX_LANA];



#define MAX_MAP_ENTRIES 20

void RPC_ENTRY
InitNBMutex (
    void
    )
/*++

Routine Description:

    This function initializes the critical section object used to serialize
    access to the global data structures.  It is called by both the client
    and server DLLs (once by each), both of which use this critical section
    object.

    Note: There is a small potential race condition in which the client and
    server could both call this function at the same time.  Given the design
    of the RPC runtime, however, this is highly unlikely if not impossible.
    Even if it were to occur, it would probably not be harmful.
--*/
{
#ifdef WIN32RPC
    static char AlreadyDone = 0;

    if (! AlreadyDone)
        {
        AlreadyDone = 1;

        InitializeCriticalSection(&NetBiosMutex);
        }
#endif // WIN32RPC
}

RPC_STATUS
MapProtocol(
    IN RPC_CHAR *ProtoSeq,
    IN int DriverNumber,
    OUT PPROTOCOL_MAP *ProtocolEntry
    )

/*++

Routine Description:

    This function maps a protocol string into a protocol map entry.

    In the non-NT versions, it looks up the information dynamically
    in the registry.  In the NT version, the registry information is
    pre-loaded

Arguments:

    ProtoSeq - the protocol sequence that we want to map

    DriverNumber - the logical driver number for the protocol.

    ProtocolEntry - pointer to place to return the results.

Return Value:

    RPC_S_OK, RPC_S_OUT_OF_RESOURCES, RPC_S_INVALID_ENDPOINT_FORMAT

    The output pointer is set to the corresponding entry when found.

--*/
{
    long status;
    int i;
    HKEY RegHandle;
    char Protocol[40];
    char LanaString[10];
    DWORD dtype;
    long BufferLength = sizeof(LanaString);

    // Copy the possible unicode protocol string to ascii.

    for (i = 0; (Protocol[i] = (char) ProtoSeq[i]) && i < sizeof(Protocol); i++) ;

    // Add the logical driver number to the protocol string.  This
    // allows multiple drivers (net cards) to be attached to the same
    // logical protocol.

    Protocol[i] = (char) ('0' + DriverNumber);
    Protocol[i+1] = 0;

    // First look in the proto sequences that we have already mapped.

    for (i = 0; ProtocolTable[i].ProtoSeq && i < MAX_LANA; i++)
        {
        // If found, set the output pointer.

        if (strcmp(ProtocolTable[i].ProtoSeq, Protocol) == 0)
            {
            *ProtocolEntry = &ProtocolTable[i];
            return(RPC_S_OK);
            }
        }

    if (i >= MAX_LANA)
        return(RPC_S_PROTSEQ_NOT_FOUND);

    status = RegOpenKeyEx(RPC_REG_ROOT, REG_NETBIOS, 0, KEY_READ, &RegHandle);

    if (status)
        return(RPC_S_PROTSEQ_NOT_FOUND);

    status = RegQueryValueExA(RegHandle, Protocol, 0, &dtype, LanaString, &BufferLength);

    RegCloseKey(RegHandle);

    if (status || ! (*LanaString >= '0' && *LanaString <= '9'))
        return(RPC_S_PROTSEQ_NOT_FOUND);

    // Now we have a Lana number for the protocol sequence.  Put this
    // info in the protocol to lana mapping structure.

    if (! (ProtocolTable[i].ProtoSeq
           = (char *) I_RpcAllocate(strlen(Protocol)+1)) )
        {
        return(RPC_S_OUT_OF_RESOURCES);
        }

    strcpy(ProtocolTable[i].ProtoSeq, Protocol);
    ProtocolTable[i].Lana = (unsigned char) (*LanaString - '0');

    *ProtocolEntry = &ProtocolTable[i];

    return(RPC_S_OK);
}


RPC_STATUS RPC_ENTRY
MapErrorCode (
    IN ERROR_TABLE * MapTable,
    IN RPC_OS_ERROR Status,
    IN RPC_STATUS DefaultStatus
    )
/*++

Routine Description:

    This function maps a OS specific error code into a generic RPC
    status code.  The ERROR_TABLE is an unordered list of pairs, with
    a value of 0 terminating the table.  So don't try to translate a
    0 OS_ERROR.

    You will see this routine called , with the return value ignored.
    This is done when we don't need the translated error code, but
    want to check for unexpected failures.

Arguments:

    MapTable - The table to OS codes to RPC_STATUS codes.

    Status - The OS specific error that we wish to map.

    DefaultStatus - The status to return if none of the codes match.
        We will ASSERT in the debug version if we have to take this action.

Return Value:

    The translated error code if there is match, else the value
    of DefaultStatus.

--*/
{
    int TableIndex;

    ASSERT(MapTable);

#ifdef DEBUGRPC
    PrintToDebugger("RPC NetBIOS Map Status %x(%d)\n", Status, DefaultStatus);
#endif

    for (TableIndex = 0; MapTable[TableIndex].OScode != 0; TableIndex++)
        if (MapTable[TableIndex].OScode == Status)
            return (MapTable[TableIndex].RpcStatus);

    return (DefaultStatus);
}


int
SetupNetBios (
    IN RPC_CHAR * RpcProtocolSequence
    )

/*++

Routine Description:

    Loadable transport initialization function.  Get the machine name
    of this workstation into MachineName and perform other OS specific
    initiailization.

Arguments:

    RpcProtocolSequence - the protocol string that mapped to this library.

Returns:

    TRUE if the initialization was OK.

--*/

{
    int i;
    BOOLEAN BooleanStatus;
    DWORD Status;
    DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
    unsigned char ComputerName[MAX_COMPUTERNAME_LENGTH + 1];

    Status = GetComputerNameA(ComputerName, &ComputerNameLength);

    ASSERT(Status == TRUE);

    _strupr(ComputerName);

    // Store padded machine name (and save unpadded length)

    MachineNameLengthUnpadded = strlen(ComputerName);
    memcpy(MachineName, ComputerName, MachineNameLengthUnpadded);
    memset(MachineName + MachineNameLengthUnpadded,
           NETBIOS_NAME_PAD_BYTE,
           sizeof(MachineName) - MachineNameLengthUnpadded);

    ASSERT(MachineNameLengthUnpadded < sizeof(MachineName));

    PUNUSED(RpcProtocolSequence);

    return(1);
}



UCHAR RPC_ENTRY
AdapterReset (
    IN PPROTOCOL_MAP ProtocolEntry
    )
/*++

Routine Description:

    This function is used by both the client and server NetBIOS transports
    to submit the RESET NCB for a process, as is required by NetBIOS on NT.

    We need to perform this in a single place to allow a process to act
    as both an RPC client and server over NetBIOS.  Otherwise, both the
    client and server transports would submit RESETs and the second one
    would destroy the state of the first.

Arguments:

    ProtocolEntry - This is the entry in the protocol map for the adapter
        to be reset.

Returns:

    The return code of the RESET NCB, if it's submitted.

    0 if the RESET has already occurred.
--*/
{
    unsigned char status = 0;

    // Make sure that no other threads are accessing this function
    CRITICAL_ENTER();

    // Perform the reset, if necessary
    if (! ProtocolEntry->ResetDone)
        {
        NCB theNCB;

        memset(&theNCB, 0 , sizeof(theNCB));
        theNCB.ncb_lana_num = ProtocolEntry->Lana;

        // The values are needed for the server
        theNCB.ncb_callname[0] = 254;       // max sessions
        theNCB.ncb_callname[1] = 64;        // max commands
        theNCB.ncb_callname[2] = 32;        // max names

        status = execNCB(NCBRESET, &theNCB);

        // If the NCB was successfully invoked, get the status from the
        // completed NCB itself.
        if (status == 0)
            {
            status = theNCB.ncb_retcode;
            }

        // Set the flag so the reset is performed only once (per process).
        if (status == 0)
            {
            ProtocolEntry->ResetDone = 1;
            }
        }

    // Allow other threads to run again
    CRITICAL_LEAVE();

    return status;
}


unsigned char RPC_ENTRY
execNCB(
    IN unsigned char command,
    IN OUT NCB *pNCB
    )
/*++

Routine Description:

    Pass an NCB to the NetBios software to execute.  This function hides
    the differences of the OS from the other parts of the driver.

Arguments:

    command - command to execute

    pNCB - NCB to act on

Returns:

    The result of ncb_retcode field from the NCB executed.

--*/
{
    unsigned char result;

    pNCB->ncb_command = command;

#if defined(WIN32RPC)
    result = Netbios(pNCB);

#elif defined(WIN)
    // If the command is an async one, call the transport helper
    // functions to corrdinate the waiting.

   _asm
        {
        les  bx,pNCB
        call NetBiosCall

        mov  al,es:[bx+1]
        mov  result,al
        }

    if (result == NRC_PENDING)
        result = 0;

#else // DOS
    _asm {
	les	bx, pNCB
        int     05ch            ; call NetBios Directly
        mov     result,al
    };

    if (result == NRC_PENDING)
        result = 0;

#endif

    return(result);
}