/*++

Copyright (c) 1994  Microsoft Corporation

Module Name:

    svccom.cxx

Abstract:

    Contains code that is common to both client and server side of
    the service location protocol..

Author:

    Madan Appiah (madana)  15-May-1995

Environment:

    User Mode - Win32

Revision History:

--*/

#include <svcloc.hxx>

DWORD g_cInitializers = 0;

//
// include global.h one more time to alloc global data.
//

//
// to enable second time include.
//

#undef _GLOBAL_
#undef EXTERN

//
// to allocate data
//

#define GLOBAL_SVC_DATA_ALLOCATE

#include <global.h>

DWORD
MakeSapServiceName(
    LPSTR SapNameBuffer,
    DWORD SapNameBufferLen
    )
/*++

Routine Description:

    This routine generates a sap service name. The first part of the name
    is computername and last part is the string version of service guid.

Arguments:

    SapNameBuffer - pointer to a sap name buffer where sap name is
        returned.

    SapNameBufferLen - length of the above buffer.

Return Value:

    pointer to sap service name..

--*/
{
    TcpsvcsDbgAssert( SapNameBufferLen >= (SAP_SERVICE_NAME_LEN + 1));

    if( SapNameBufferLen < SAP_SERVICE_NAME_LEN + 1) {
        return( ERROR_INSUFFICIENT_BUFFER );
    }

    //
    // Get Computername.
    //


    DWORD Len = SapNameBufferLen;

    if( !GetComputerNameA( SapNameBuffer, &Len ) ) {

        DWORD Error = GetLastError();

        TcpsvcsDbgPrint(( DEBUG_ERRORS,
            "GetComputerNameA failed, %ld.\n", Error ));

        return( Error );
    }

    TcpsvcsDbgAssert( Len <= MAX_COMPUTERNAME_LENGTH );

    while( Len < MAX_COMPUTERNAME_LENGTH ) {
        SapNameBuffer[Len++] = '!';
    }

    //
    // append GUID.
    //

    strcpy( SapNameBuffer + Len, SERVICE_GUID_STR );

     return( ERROR_SUCCESS );
}

VOID
MakeUniqueServerName(
    LPBYTE StrBuffer,
    DWORD StrBufferLen,
    LPSTR ComputerName
    )
/*++

Routine Description:

    This routine makes an unique name used by the server to listen to
    the client discovery requests.

Arguments:

    StrBuffer : pointer to a buffer where the unique name is returned.

    StrBufferLen : length of the above buffer.

    ComputerName : pointer to the computer that is used to form the unique
        name.

Return Value:

    none.

--*/
{
    DWORD ComputerNameLen = strlen(ComputerName);
    DWORD BufLen = StrBufferLen;
    LPBYTE Buffer = StrBuffer;

    memset( Buffer, 0x0, BufLen );

    memcpy(
        Buffer,
        NETBIOS_INET_SERVER_UNIQUE_NAME,
        NETBIOS_INET_SERVER_UNIQUE_NAME_LEN );

    BufLen -= NETBIOS_INET_SERVER_UNIQUE_NAME_LEN;
    Buffer += NETBIOS_INET_SERVER_UNIQUE_NAME_LEN;

    if( BufLen >= ComputerNameLen  ) {

        //
        // we enough space in the buffer to append computername.
        //

        memcpy( Buffer,ComputerName, ComputerNameLen );
        return;
    }

    //
    // buffer does not have enough space, chop off few chars from the
    // begining of the computername.
    //

    memcpy( Buffer, ComputerName + (ComputerNameLen - BufLen), BufLen );
    return;
}

#if 0

VOID
AppendRandomChar(
    LPSTR String
    )
/*++

Routine Description:

    This routine adds a random char to the end of the given string. It is
    assumed that the given string has a space for the new char.

Arguments:

    String : pointer to a string where a random char is added.

Return Value:

    none.

--*/
{
    CHAR RandChar;
    DWORD RandNum;

    RandNum = (DWORD)rand();

    RandNum = RandNum % (26 + 10);  // 26 alphabets, and 10 numerics

    if( RandNum < 10 ) {
        RandChar = (CHAR)('0'+ RandNum);
    }
    else {
        RandChar = (CHAR)('A'+ RandNum - 10);
    }

    DWORD Len = strlen(String);

    //
    // append random char.
    //

    String[Len] = RandChar;
    String[Len + 1] = '\0';
    return;
}

#endif //0

DWORD
ComputeCheckSum(
    LPBYTE Buffer,
    DWORD BufferLength
    )
/*++

Routine Description:

    This function computes the check sum of the given buffer by ex-or'ing
    the dwords. It is assumed that the buffer DWORD aligned and the buffer
    length is multiples of DWORD.

Arguments:

    Buffer : pointer to a buffer whose check sum to be computed.

    BufferLength : length of the above buffer.

Return Value:

    Check sum.

--*/
{
    DWORD CheckSum = 0;
    LPDWORD BufferPtr = (LPDWORD)Buffer;
    LPBYTE EndBuffer = Buffer + BufferLength;

    TcpsvcsDbgAssert( (ULONG_PTR)Buffer % sizeof(DWORD) == 0 );
        // alignment check.

    TcpsvcsDbgAssert( BufferLength % sizeof(DWORD) == 0 );
        // multiple DWORD check.

    while( (LPBYTE)BufferPtr < EndBuffer ) {
        CheckSum ^= *BufferPtr;
        BufferPtr++;
    }

    return( CheckSum );
}

BOOL
DLLSvclocEntry(
    IN HINSTANCE DllHandle,
    IN DWORD Reason,
    IN LPVOID Reserved
    )
/*++

Routine Description:

    Performs global initialization and termination for all protocol modules.

    This function only handles process attach and detach which are required for
    global initialization and termination, respectively. We disable thread
    attach and detach. New threads calling Wininet APIs will get an
    INTERNET_THREAD_INFO structure created for them by the first API requiring
    this structure

Arguments:

    DllHandle   - handle of this DLL. Unused

    Reason      - process attach/detach or thread attach/detach

    Reserved    - if DLL_PROCESS_ATTACH, NULL means DLL is being dynamically
                  loaded, else static. For DLL_PROCESS_DETACH, NULL means DLL
                  is being freed as a consequence of call to FreeLibrary()
                  else the DLL is being freed as part of process termination

Return Value:

    BOOL
        Success - TRUE

        Failure - FALSE. Failed to initialize

--*/

{
    BOOL ok;
    DWORD error;

    UNREFERENCED_PARAMETER(DllHandle);

    //
    // perform global dll initialization, if any.
    //

    switch (Reason) {
    case DLL_PROCESS_ATTACH:

        //
        // we switch off thread library calls to avoid taking a hit for every
        // thread creation/termination that happens in this process, regardless
        // of whether Internet APIs are called in the thread.
        //
        // If a new thread does make Internet API calls that require a per-thread
        // structure then the individual API will create one
        //

        DisableThreadLibraryCalls(DllHandle);

        //
        //  Old Normandy servers that are in our process are assuming the
        //  service locator is initialized by DLL_PROCESS_ATTACH and terminated
        //  by PROCESS_DETACH.  Since the service locator has a thread we
        //  can't safely cleanup during process_detach so we do an extra
        //  loadlibrary on ourselves and remain in process.  When the Normandy
        //  servers are updated, remove this.
        //

        if ( !InitSvcLocator() ||
             !LoadLibrary( "inetsloc.dll" ))
        {
            return FALSE;
        }

        break;

    case DLL_PROCESS_DETACH:

        break;
    }

    return (TRUE);
}

BOOL
InitSvcLocator(
    VOID
    )
{
    //
    //  We assume the caller is serializing access from multiple initializers.
    //  The callers will presumably be just infocomm.dll that does do the
    //  serialization.
    //
    //

    if ( g_cInitializers++ ) {
        return TRUE;
    }

    if ( DllProcessAttachSvcloc() != ERROR_SUCCESS ) {
        return FALSE;
    }

    return TRUE;
}

BOOL
TerminateSvcLocator(
    VOID
    )
{
    if ( --g_cInitializers )
        return TRUE;

    DllProcessDetachSvcloc();

    return TRUE;
}



DWORD
DllProcessAttachSvcloc(
    VOID
    )
/*++

Routine Description:

    This dll init function initializes service location global variables.

Arguments:

    NONE.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;

    //
    // initialize global variables.
    //

    // DebugBreak();

#if DBG

    //
    // initialize dbg crit sect.
    //

    INITIALIZE_CRITICAL_SECTION( &GlobalDebugCritSect );
    GlobalDebugFlag = DEBUG_ERRORS;

#endif // DBG

    INITIALIZE_CRITICAL_SECTION( &GlobalSvclocCritSect );

    LOCK_SVC_GLOBAL_DATA();
    GlobalComputerName[0] = '\0';

    GlobalSrvRegistered = FALSE;
    SvclocHeap = new MEMORY;

    if( SvclocHeap == NULL ) {
        UNLOCK_SVC_GLOBAL_DATA();
        return( ERROR_NOT_ENOUGH_MEMORY );
    }

    GlobalSrvInfoObj = NULL;

    GlobalSrvRespMsg = NULL;
    GlobalSrvRespMsgLength = 0;
    GlobalSrvAllotedRespMsgLen = 0;

    GlobalWinsockStarted = FALSE;
    GlobalRNRRegistered = FALSE;

    GlobalSrvListenThreadHandle = NULL;

    memset( &GlobalSrvSockets, 0x0, sizeof(GlobalSrvSockets) );

    GlobalCliDiscoverThreadHandle = NULL;

    GlobalCliQueryMsg = NULL;
    GlobalCliQueryMsgLen = 0;

    GlobalSapGuid.Data1 = ssgData1;
    GlobalSapGuid.Data2 = ssgData2;
    GlobalSapGuid.Data3 = ssgData3;
    GlobalSapGuid.Data4[0] = ssgData41;
    GlobalSapGuid.Data4[1] = ssgData42;
    GlobalSapGuid.Data4[2] = ssgData43;
    GlobalSapGuid.Data4[3] = ssgData44;
    GlobalSapGuid.Data4[4] = ssgData45;
    GlobalSapGuid.Data4[5] = ssgData46;
    GlobalSapGuid.Data4[6] = ssgData47;
    GlobalSapGuid.Data4[7] = ssgData48;

    InitializeListHead( &GlobalCliQueryRespList );

    memset( &GlobalCliSockets, 0x0, sizeof(GlobalCliSockets) );
    memset( &GlobalCliNBSockets, 0x0, sizeof(GlobalCliNBSockets) );
    GlobalCliIpxSocket = INVALID_SOCKET;

    GlobalDiscoveryInProgressEvent =
        IIS_CREATE_EVENT(
            "GlobalDiscoveryInProgressEvent",
            &GlobalDiscoveryInProgressEvent,
            TRUE,       // MANUAL reset
            TRUE        // initial state: signalled
            );

    if ( GlobalDiscoveryInProgressEvent == NULL ) {
        Error = GetLastError();
        UNLOCK_SVC_GLOBAL_DATA();
        return(Error);
    }

    GlobalLastDiscoveryTime = 0;

    //
    // get platform type.
    //

    OSVERSIONINFO VersionInfo;

    VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);

    if ( (GetVersionEx(&VersionInfo)) ) {
        GlobalPlatformType = VersionInfo.dwPlatformId;
    }
    else {
        UNLOCK_SVC_GLOBAL_DATA();
        return( ERROR_INSUFFICIENT_BUFFER );
    }

    GlobalNumNBPendingRecvs = 0;
    GlobalNBPendingRecvs = NULL;

    InitializeListHead( &GlobalWin31NBRespList );
    GlobalWin31NumNBResps = 0;

    UNLOCK_SVC_GLOBAL_DATA();
    srand( (unsigned)time(NULL));

    return( ERROR_SUCCESS );
}

DWORD
DllProcessDetachSvcloc(
    VOID
    )
/*++

Routine Description:

    This fundtion frees the global service location objects.

Arguments:

    NONE.

Return Value:

    Windows Error Code.

--*/
{
    LOCK_SVC_GLOBAL_DATA();

    if( GlobalSrvRegistered ) {
        ServerDeregisterAndStopListen();
    }

    if( GlobalSrvInfoObj != NULL ) {
        delete GlobalSrvInfoObj;
        GlobalSrvInfoObj = NULL;
    }

    if( GlobalSrvRespMsg != NULL ) {
        SvclocHeap->Free( GlobalSrvRespMsg );
        GlobalSrvRespMsg = NULL;
        GlobalSrvRespMsgLength = 0;
        GlobalSrvAllotedRespMsgLen = 0;
    }

    if( GlobalSrvRecvBuf != NULL ) {
        SvclocHeap->Free( GlobalSrvRecvBuf );
        GlobalSrvRecvBuf = NULL;
        GlobalSrvRecvBufLength = 0;
    }

    if( GlobalCliQueryMsg != NULL ) {
        SvclocHeap->Free( GlobalCliQueryMsg );
        GlobalCliQueryMsg = NULL;
        GlobalCliQueryMsgLen = 0;
    }

    InitializeListHead( &GlobalCliQueryRespList );

    while( !IsListEmpty( &GlobalCliQueryRespList ) ) {

        LPCLIENT_QUERY_RESPONSE QueryResponse;

        //
        // remove head entry and free it up.
        //

        QueryResponse = (LPCLIENT_QUERY_RESPONSE)
            RemoveHeadList( &GlobalCliQueryRespList );

        //
        // free response buffer.
        //

        SvclocHeap->Free( QueryResponse->ResponseBuffer );

        //
        // free this node.
        //

        SvclocHeap->Free( QueryResponse );
    }

    //
    // close client sockets.
    //

    DWORD i;

    for( i = 0; i <  GlobalCliSockets.fd_count; i++ ) {
        closesocket( GlobalCliSockets.fd_array[i] );
    }

    //
    // invalidate client handles.
    //

    memset( &GlobalCliSockets, 0x0, sizeof(GlobalCliSockets) );
    memset( &GlobalCliNBSockets, 0x0, sizeof(GlobalCliNBSockets) );
    GlobalCliIpxSocket = INVALID_SOCKET;


    //
    // stop client discovery thread.
    //

    if( GlobalCliDiscoverThreadHandle != NULL ) {

        //
        // Wait for the client discovery thread to stop, but don't wait
        // for longer than THREAD_TERMINATION_TIMEOUT msecs (60 secs)
        //

        DWORD WaitStatus =
            WaitForSingleObject(
                    GlobalCliDiscoverThreadHandle,
                    THREAD_TERMINATION_TIMEOUT );

        TcpsvcsDbgAssert( WaitStatus != WAIT_FAILED );

        if(  WaitStatus == WAIT_FAILED ) {
            TcpsvcsDbgPrint((DEBUG_ERRORS,
                "WaitForSingleObject call failed, %ld\n", GetLastError() ));
        }

        CloseHandle( GlobalCliDiscoverThreadHandle );
        GlobalCliDiscoverThreadHandle = NULL;
    }

    if( GlobalWinsockStarted ) {
        WSACleanup();
        GlobalWinsockStarted = FALSE;
    }

    TcpsvcsDbgAssert( GlobalNumNBPendingRecvs == 0)
    TcpsvcsDbgAssert( GlobalNBPendingRecvs == NULL );

    if( GlobalNBPendingRecvs != NULL ) {
        SvclocHeap->Free( GlobalNBPendingRecvs );
    }

    TcpsvcsDbgAssert( GlobalWin31NumNBResps == 0)
    TcpsvcsDbgAssert( IsListEmpty( &GlobalWin31NBRespList ) == TRUE );

    //
    // free response list.
    //

    while ( !IsListEmpty( &GlobalWin31NBRespList ) ) {

        PLIST_ENTRY Entry;

        Entry = RemoveHeadList( &GlobalWin31NBRespList );

        //
        // free response buffer if it is not used.
        //

        if( ((LPSVCLOC_NETBIOS_RESP_ENTRY)
                Entry)->Resp.ResponseBuffer != NULL ) {

            SvclocHeap->Free(
                ((LPSVCLOC_NETBIOS_RESP_ENTRY)
                    Entry)->Resp.ResponseBuffer );
        }

        SvclocHeap->Free( Entry );
    }

    if( SvclocHeap != NULL ) {
        delete SvclocHeap;
        SvclocHeap = NULL;
    }

    if( GlobalDiscoveryInProgressEvent != NULL ) {
        CloseHandle( GlobalDiscoveryInProgressEvent );
        GlobalDiscoveryInProgressEvent = NULL;
    }

    GlobalLastDiscoveryTime = 0;

    UNLOCK_SVC_GLOBAL_DATA();

    DeleteCriticalSection( &GlobalSvclocCritSect );

#if DBG

    //
    // Delete dbg crit sect.
    //

    DeleteCriticalSection( &GlobalDebugCritSect );

#endif // DBG


    return( ERROR_SUCCESS );
}

VOID
FreeServiceInfo(
    LPINET_SERVICE_INFO ServiceInfo
    )
/*++

Routine Description:

    This function frees the memory blocks consumed by the service info
    structure.

Arguments:

    ServiceInfo : pointer to a service info structure.

Return Value:

    None.

--*/
{
    TcpsvcsDbgAssert( ServiceInfo != NULL );

    if( ServiceInfo == NULL ) {
        return;
    }

    //
    // free all leaves of the tree first and then branches.
    //

    //
    // free service comment.
    //

    if( ServiceInfo->ServiceComment != NULL ) {
        SvclocHeap->Free(  ServiceInfo->ServiceComment );
    }

    if( ServiceInfo->Bindings.NumBindings ) {

        TcpsvcsDbgAssert( ServiceInfo->Bindings.BindingsInfo != NULL );

        if(  ServiceInfo->Bindings.BindingsInfo != NULL ) {
            DWORD i;

            for( i = 0; i < ServiceInfo->Bindings.NumBindings; i++ ) {

                if( ServiceInfo->Bindings.BindingsInfo[i].BindData != NULL ) {
                    SvclocHeap->Free( ServiceInfo->Bindings.BindingsInfo[i].BindData );
                }
            }

            SvclocHeap->Free(  ServiceInfo->Bindings.BindingsInfo );
        }
    }
    else {
        TcpsvcsDbgAssert( ServiceInfo->Bindings.BindingsInfo == NULL );
    }

    SvclocHeap->Free( ServiceInfo );

    return;
}

VOID
FreeServerInfo(
    LPINET_SERVER_INFO ServerInfo
    )
/*++

Routine Description:

    This function frees the memory blocks consumed by the server info
    structure.

Arguments:

    ServerInfo : pointer to a server info structure.

Return Value:

    None.

--*/
{
    DWORD i;

    if( ServerInfo != NULL ) {

        //
        // first free all service info.
        //

        if( ServerInfo->Services.NumServices > 0 ) {
            TcpsvcsDbgAssert( ServerInfo->Services.Services != NULL );
        }
        for ( i = 0; i < ServerInfo->Services.NumServices; i++) {

            FreeServiceInfo( ServerInfo->Services.Services[i] );
        }

        //
        // now free services pointer array.
        //

        if( ServerInfo->Services.Services != NULL ) {
            SvclocHeap->Free( ServerInfo->Services.Services );
        }

        //
        // free server address.
        //

        if( ServerInfo->ServerAddress.BindData != NULL ) {
            SvclocHeap->Free( ServerInfo->ServerAddress.BindData );
        }

        //
        // free server name.
        //

        if( ServerInfo->ServerName != NULL ) {
            SvclocHeap->Free( ServerInfo->ServerName );
        }

        //
        // now server info structure.
        //

        SvclocHeap->Free( ServerInfo );
    }

    return;
}

VOID
FreeServersList(
    LPINET_SERVERS_LIST ServersList
    )
/*++

Routine Description:

    This function frees the memory blocks consumed by the servers list
    structure.

Arguments:

    ServersList : pointer to a servers liststructure.

Return Value:

    None.

--*/
{
    if( ServersList != NULL ) {

        //
        // free server info structures.
        //

        if( ServersList->NumServers > 0 ) {
            TcpsvcsDbgAssert( ServersList->Servers != NULL );
        }

        DWORD i;

        for( i = 0; i < ServersList->NumServers; i++ ) {
            FreeServerInfo( ServersList->Servers[i] );
        }

        //
        // free servers info pointer array.
        //
        if( ServersList->Servers != NULL ) {
            SvclocHeap->Free( ServersList->Servers );
        }


        //
        // servers list structure.
        //

        SvclocHeap->Free( ServersList );
    }

    return;
}

BOOL
GetNetBiosLana(
    PLANA_ENUM pLanas
    )
/*++

Routine Description:

    This function enumurate all netbios lana on the system.

Arguments:

    pLanas - pointer to LANA_ENUM structure where enum is returned.

Return Value:

    TRUE - if successed.
    FALSE - otherwise.

--*/
{
    NCB NetBiosNCB;
    UCHAR NBErrorCode;

    memset( &NetBiosNCB, 0,  sizeof(NetBiosNCB) );
    NetBiosNCB.ncb_command = NCBENUM;
    NetBiosNCB.ncb_buffer = (PUCHAR)pLanas;
    NetBiosNCB.ncb_length = sizeof(LANA_ENUM);

    NBErrorCode = Netbios( &NetBiosNCB );

    if( (NBErrorCode == NRC_GOODRET) &&
        (NetBiosNCB.ncb_retcode == NRC_GOODRET) ) {

        return( TRUE );
    }

    TcpsvcsDbgPrint(( DEBUG_ERRORS, "NetBios() failed, %ld, %ld \n",
        NBErrorCode, NetBiosNCB.ncb_retcode ));

    return( FALSE );
}


BOOL
GetEnumNBLana(
    PLANA_ENUM pLanas
    )
/*++

Routine Description:

    This function enumurate all netbios lana on the system.

Arguments:

    pLanas - pointer to LANA_ENUM structure where enum is returned.

Return Value:

    TRUE - if successed.
    FALSE - otherwise.

--*/
{
    DWORD Error;
    INT ProtocolCount;
    PPROTOCOL_INFO ProtocolBuffer = NULL;
    DWORD ProtocolBufferSize = 0;

    //
    // init return value.
    //

    pLanas->length = 0;

    //
    // determine the enum buffer size required.
    //

    ProtocolCount = EnumProtocols(
                        NULL,
                        NULL,
                        &ProtocolBufferSize );

    if( ProtocolCount == SOCKET_ERROR ) {

        Error = WSAGetLastError();

        if( Error != ERROR_INSUFFICIENT_BUFFER ) {
            goto Cleanup;
        }
    }

    if( (ProtocolBufferSize == 0) || (ProtocolCount == 0) ) {

        Error = ERROR_SUCCESS;
        goto Cleanup;
    }

    //
    // allocate memory for the protocol buffer.
    //

    ProtocolBuffer =
        (PPROTOCOL_INFO)SvclocHeap->Alloc( ProtocolBufferSize );

    if( ProtocolBuffer == NULL ) {
        Error = ERROR_NOT_ENOUGH_MEMORY;
        goto Cleanup;
    }

    //
    // now enum protocols.
    //

    ProtocolCount = EnumProtocols(
                        NULL,
                        ProtocolBuffer,
                        &ProtocolBufferSize );

    if( ProtocolCount == SOCKET_ERROR ) {

        Error = WSAGetLastError();
        goto Cleanup;
    }

    TcpsvcsDbgAssert( ProtocolCount > 0 );

    //
    // now filter net bios protcols only and get the corresponding lana
    // values.
    //

    DWORD i;
    for ( i = 0; i < (DWORD)ProtocolCount; i++ ) {

        if( ProtocolBuffer[i].iAddressFamily == AF_NETBIOS ) {

            if( pLanas->length < MAX_LANA ) {

                UCHAR Lana;
                DWORD j;

                Lana = (UCHAR)((INT)ProtocolBuffer[i].iProtocol * (-1));

                //
                // if this is a new lana add to list.
                //

                for ( j = 0; j < pLanas->length ; j++ ) {
                    if( pLanas->lana[j] == Lana ) {
                        break;
                    }
                }

                if( j >= pLanas->length ) {
                    pLanas->lana[pLanas->length] = Lana;
                    pLanas->length++;
                }
            }
        }
    }

    Error = ERROR_SUCCESS;

Cleanup:

    if( ProtocolBuffer != NULL ) {
        SvclocHeap->Free( ProtocolBuffer );
    }

    if( Error != ERROR_SUCCESS ) {
        TcpsvcsDbgPrint(( DEBUG_ERRORS,
            "GetNetBiosLana failed, %ld\n", Error ));
        return( FALSE );
    }

    return( TRUE );
}


BOOL
MakeNBSocketForLana(
    UCHAR Lana,
    PSOCKADDR  pSocketAddress,
    SOCKET *pNBSocket
    )
/*++

Routine Description:

    This function possibly creates a socket for the given lana and binds
    to the given socket address.

    ASSUME : global data crit sect is locked.

Arguments:

    Lana : lana number for the new sockets.

    pSocketAddress : pointer to a socket address to bind to.

    pNBSocket : pointer to a location where the new socket is returned.

Return Value:

    TRUE : if successfully created a socket and bound to the given nb
        addresse.
    FALSE : otherwise.

--*/
{
    DWORD Error;
    SOCKET NBSocket;
    DWORD Arg = 1;

    *pNBSocket = INVALID_SOCKET;

    //
    // create a socket for this lana.
    //

    NBSocket = socket( AF_NETBIOS, SOCK_DGRAM, Lana );

    if( NBSocket == INVALID_SOCKET ) {

        Error = WSAGetLastError();
        TcpsvcsDbgPrint(( DEBUG_ERRORS, "socket() failed, %ld\n", Error ));

        //
        // something wrong with this lana, try rest.
        //

        return( FALSE );
    }

    //
    // make this socket non blocking.
    //

    if( (ioctlsocket( NBSocket, FIONBIO, &Arg )) == SOCKET_ERROR ) {

        Error = WSAGetLastError();
        TcpsvcsDbgPrint(( DEBUG_ERRORS, "ioctlsocket() failed, %ld\n", Error ));

        //
        // something wrong with this lana, try rest.
        //

        closesocket( NBSocket );
        return( FALSE );
    }

    //
    // bind to this socket.
    //

    if( bind(
            NBSocket,
            pSocketAddress,
            sizeof(SOCKADDR_NB) ) == SOCKET_ERROR ) {

        Error = WSAGetLastError();
        TcpsvcsDbgPrint(( DEBUG_ERRORS, "ioctlsocket() failed, %ld\n", Error ));

        //
        // something wrong with this lana, try rest.
        //

        closesocket( NBSocket );
        return( FALSE );
    }

    *pNBSocket = NBSocket;
    return( TRUE );
}