/*++

Copyright(c) 1995 Microsoft Corporation

MODULE NAME
    access.c

ABSTRACT
    Address accessibility routines for automatic connections

AUTHOR
    Anthony Discolo (adiscolo) 26-Jul-1995

REVISION HISTORY

--*/

#define UNICODE
#define _UNICODE

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <stdlib.h>
#include <windows.h>
#include <tdi.h>
#include <sys\uio.h>
#include <nb30.h>
#include <nbtioctl.h>
#include <stdio.h>
#include <npapi.h>
#include <ctype.h>
#include <winsock.h>
#include <acd.h>
#include <ras.h>
#include <raserror.h>
#include <rasman.h>
#include <debug.h>
#include <ipexport.h>
#include <icmpapi.h>

#include "reg.h"
#include "rasprocs.h"
#include "misc.h"
#include "table.h"
#include "addrmap.h"
#include "imperson.h"

//
// The format of Adapter Status responses.
//
typedef struct _ADAPTERSTATUS
{
    ADAPTER_STATUS AdapterInfo;
    NAME_BUFFER    Names[32];
} ADAPTERSTATUS, *PADAPTERSTATUS;

//
// Icmp.dll library entrypoints.
//
#define ICMP_MODULE     L"icmp"
HANDLE hIcmpG;

#define ICMPCREATEFILE  "IcmpCreateFile"
FARPROC lpfnIcmpCreateFileG;

#define ICMPSENDECHO    "IcmpSendEcho"
FARPROC lpfnIcmpSendEchoG;

#define ICMPCLOSEHANDLE "IcmpCloseHandle"
FARPROC lpfnIcmpCloseHandleG;

//
// PingIpAddress constants
//
#define PING_SEND_SIZE  32
#define PING_RECV_SIZE  (0x2000 - 8)
#define PING_TTL        32
#define PING_TIMEOUT    2000L   // needs to be long enough to succeed over slow links

//
// External variables
//
extern HANDLE hTerminatingG;



BOOLEAN
CopyNetbiosName(
    IN NAME_BUFFER *pNames,
    IN DWORD dwcNames,
    OUT LPSTR pszNetbiosName
    )
{
    BOOLEAN fFound = FALSE;
    DWORD i, iWks = 0;
    CHAR szWks[NCBNAMSZ];
    PCHAR p = pszNetbiosName;

    //
    // Find the unique workstation name.
    //
again:
    szWks[0] = '\0';
    for (i = iWks; i < dwcNames; i++) {
        TRACE2(
          "CopyNetbiosName: wks %15.15s (0x%x)",
          pNames[i].name,
          pNames[i].name[NCBNAMSZ - 1]);
        if (pNames[i].name[NCBNAMSZ - 1] == 0x0 &&
            !(pNames[i].name_flags & GROUP_NAME))
        {
            TRACE1("CopyNetbiosName: iWks=%d\n", iWks);
            iWks = i;
            memcpy(szWks, pNames[i].name, NCBNAMSZ - 1);
            break;
        }
    }
    //
    // Check to make sure we found one.
    //
    if (szWks[0] == '\0')
        return FALSE;
    //
    // Find the unique server name and make
    // sure it matches the workstation name.
    //
    for (i = 0; i < dwcNames; i++) {
        TRACE3(
          "CopyNetbiosName: srv %15.15s (0x%x), cmp=%d",
          pNames[i].name,
          pNames[i].name[NCBNAMSZ - 1],
          memcmp(szWks, pNames[i].name, NCBNAMSZ - 1));
        if (pNames[i].name[NCBNAMSZ - 1] == 0x20 &&
            !(pNames[i].name_flags & GROUP_NAME) &&
            !memcmp(szWks, pNames[i].name, NCBNAMSZ - 1))
        {
            DWORD j;

            //
            // Copy up to a null or a space.
            //
            for (j = 0; j < NCBNAMSZ - 1; j++) {
                if (pNames[i].name[j] == '\0' || pNames[i].name[j] == ' ')
                    break;
                *p++ = pNames[i].name[j];
            }
            *p++ = '\0';
            return TRUE;
        }
    }
    //
    // No match found.  Look for another unique workstation
    // name and try again if we haven't exhausted the list.
    //
    if (++iWks >= dwcNames)
        return FALSE;
    goto again;
} // CopyNetbiosName



LPTSTR
IpAddressToNetbiosName(
    IN LPTSTR pszIpAddress,
    IN HPORT hPort
    )
{
    BOOLEAN fFound;
    RAS_PROTOCOLS Protocols;
    DWORD i, dwcProtocols;
    RASMAN_ROUTEINFO *pRoute;
    WCHAR szAdapterName[MAX_PATH];
    NTSTATUS status;
    HANDLE fd;
    OBJECT_ATTRIBUTES objectAttributes;
    IO_STATUS_BLOCK ioStatusBlock;
    UNICODE_STRING unicodeString;
    ULONG ipaddr;
    CHAR szIpAddress[17];
    tIPANDNAMEINFO ipAndNameInfo;
    PVOID pBuffer;
    DWORD dwSize;
    PADAPTERSTATUS pAdapterStatus;
    NAME_BUFFER *pNames;
    DWORD dwcNames;
    LPTSTR pszNetbiosName = NULL;

    //
    // Enumerate the bindings for the port to
    // try to find the Netbt device.
    //
    GetPortProtocols(hPort, &Protocols, &dwcProtocols);
    fFound = FALSE;
    for (i = 0; i < dwcProtocols; i++) {
        pRoute = &Protocols.RP_ProtocolInfo[i];
        TRACE3(
          "IpAddressToNetbiosName: adapter type=%d, name=%S, xport=%S",
          pRoute->RI_Type,
          pRoute->RI_AdapterName,
          pRoute->RI_XportName);
        if (pRoute->RI_Type == IP) {
            wcscpy(szAdapterName, L"\\Device\\Netbt_");
            wcscat(szAdapterName, &pRoute->RI_AdapterName[8]);
            fFound = TRUE;
            break;
        }
    }
    if (!fFound)
        return NULL;
    //
    // Open the device and issue a remote
    // adapter status command.
    //
    RtlInitUnicodeString(&unicodeString, szAdapterName);
    InitializeObjectAttributes(
      &objectAttributes,
      &unicodeString,
      OBJ_CASE_INSENSITIVE,
      NULL,
      NULL);
    status = NtCreateFile(
               &fd,
               SYNCHRONIZE|FILE_READ_DATA|FILE_WRITE_DATA,
               &objectAttributes,
               &ioStatusBlock,
               NULL,
               FILE_ATTRIBUTE_NORMAL,
               FILE_SHARE_READ|FILE_SHARE_WRITE,
               FILE_OPEN_IF,
               0,
               NULL,
               0);
    if (!NT_SUCCESS(status)) {
        TRACE1(
          "IpAddressToNetbiosName: NtCreateFile failed (status=0x%x)\n",
          status);
        return NULL;
    }

    UnicodeStringToAnsiString(pszIpAddress, szIpAddress, sizeof (szIpAddress));
    ipaddr = inet_addr(szIpAddress);
    if (ipaddr == INADDR_ANY)
        return NULL;

    RtlZeroMemory(&ipAndNameInfo, sizeof (ipAndNameInfo));
    ipAndNameInfo.IpAddress = ntohl(ipaddr);
    ipAndNameInfo.NetbiosAddress.Address[0].Address[0].NetbiosName[0] = '*';
    ipAndNameInfo.NetbiosAddress.TAAddressCount = 1;
    ipAndNameInfo.NetbiosAddress.Address[0].AddressLength =
      sizeof (TDI_ADDRESS_NETBIOS);
    ipAndNameInfo.NetbiosAddress.Address[0].AddressType =
      TDI_ADDRESS_TYPE_NETBIOS;
    ipAndNameInfo.NetbiosAddress.Address[0].Address[0].NetbiosNameType =
      TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;

    dwSize = 2048;
    for (;;) {
        pBuffer = LocalAlloc(LPTR, dwSize);
        if (pBuffer == NULL) {
            TRACE("IpAddressToNetbiosName: LocalAlloc failed");
            return NULL;
        }
        status = NtDeviceIoControlFile(
                   fd,
                   NULL,
                   NULL,
                   NULL,
                   &ioStatusBlock,
                   IOCTL_NETBT_ADAPTER_STATUS,
                   &ipAndNameInfo,
                   sizeof (tIPANDNAMEINFO),
                   pBuffer,
                   dwSize);
        if (status != STATUS_BUFFER_OVERFLOW)
            break;

        LocalFree(pBuffer);
        dwSize *= 2;
        if (dwSize >= 0xffff) {
            TRACE("IpAddressToNetbiosName: Unable to allocate packet");
            return NULL;
        }
    }
    if (status == STATUS_PENDING) {
        status = NtWaitForSingleObject(fd, TRUE, NULL);
        if (status == STATUS_SUCCESS)
            status = ioStatusBlock.Status;
    }
    NtClose(fd);

    pAdapterStatus = (PADAPTERSTATUS)pBuffer;
    dwcNames = pAdapterStatus->AdapterInfo.name_count;
    TRACE2(
      "IpAddressToNetbiosName: results (status=0x%x, dwcNames=%d)\n",
      status,
      dwcNames);
    if (status == STATUS_SUCCESS && dwcNames) {
        CHAR szNetbiosName[NCBNAMSZ + 1];

        pNames = pAdapterStatus->Names;
        if (CopyNetbiosName(pNames, dwcNames, szNetbiosName))
            pszNetbiosName = AnsiStringToUnicodeString(szNetbiosName, NULL, 0);
    }
    LocalFree(pBuffer);

    return pszNetbiosName;
} // IpAddressToNetbiosName



UCHAR
HexByte(
    IN PCHAR p
    )
{
    UCHAR c;

    c = *(UCHAR *)p;
    if (c >= '0' && c <= '9')
        return c - '0';
    if ((c >= 'A' && c <= 'F') ||
        (c >= 'a' && c <= 'f'))
    {
        return c - ('A' - 10);
    }
    return 0xff;
} // HexByte



VOID
StringToNodeNumber(
    IN PCHAR pszString,
    OUT PCHAR pszNode
    )
{
    UCHAR c1, c2;
    INT i;

    if (strlen(pszString) != 12) {
        TRACE("StringToNodeNumber: bad node number length\n");
        return;
    }
    for (i = 0; i < 6; i++) {
        c1 = HexByte(pszString++);
        c2 = HexByte(pszString++);
        if (c1 == 0xff || c2 == 0xff) {
            TRACE("StringToNodeNumber: bad digit");
            return;
        }
        *pszNode++ = (c1 << 4) + c2;
    }
} // StringToNodeNumber



VOID
NodeNumberToString(
    IN PCHAR pszNode,
    OUT PCHAR pszString
    )
{
    UCHAR c1, c2;
    INT i;

    sprintf(
      pszString,
      "%02x:%02x:%02x:%02x:%02x:%02x",
      pszNode[0],
      pszNode[1],
      pszNode[2],
      pszNode[3],
      pszNode[4],
      pszNode[5]);
} // NodeNumberToString



LPTSTR
IpxAddressToNetbiosName(
    IN LPTSTR pszIpxAddress
    )
{
    NTSTATUS status;
    HANDLE fd;
    OBJECT_ATTRIBUTES objectAttributes;
    IO_STATUS_BLOCK ioStatusBlock;
    UNICODE_STRING unicodeString;
    PTDI_REQUEST_QUERY_INFORMATION pQuery;
    PTDI_CONNECTION_INFORMATION pConnectionInformation;
    PTA_NETBIOS_ADDRESS pRemoteAddress;
    CHAR szIpxAddress[13];
    PVOID pBuffer;
    DWORD dwQuerySize, dwBufferSize;
    PADAPTERSTATUS pAdapterStatus;
    NAME_BUFFER *pNames;
    DWORD dwcNames;
    LPTSTR pszNetbiosName = NULL;

    RtlInitUnicodeString(&unicodeString, L"\\Device\\Nwlnknb");
    InitializeObjectAttributes(
      &objectAttributes,
      &unicodeString,
      OBJ_CASE_INSENSITIVE,
      NULL,
      NULL);
    status = NtCreateFile(
               &fd,
               SYNCHRONIZE|FILE_READ_DATA|FILE_WRITE_DATA,
               &objectAttributes,
               &ioStatusBlock,
               NULL,
               FILE_ATTRIBUTE_NORMAL,
               FILE_SHARE_READ|FILE_SHARE_WRITE,
               FILE_OPEN_IF,
               0,
               NULL,
               0);
    if (!NT_SUCCESS(status)) {
        TRACE1("IpxAddressToNetbiosName: NtCreateFile failed (status=0x%x)", status);
        return NULL;
    }

    dwQuerySize = sizeof (TDI_REQUEST_QUERY_INFORMATION) +
                    sizeof (TDI_CONNECTION_INFORMATION) +
                    sizeof (TA_NETBIOS_ADDRESS);
    pQuery = LocalAlloc(LPTR, dwQuerySize);
    if (pQuery == NULL) {
        TRACE("IpxAddressToNetbiosName: LocalAlloc failed");
        return NULL;
    }
    pQuery->QueryType = TDI_QUERY_ADAPTER_STATUS;
      (PTDI_CONNECTION_INFORMATION)&pQuery->RequestConnectionInformation;
    pQuery->RequestConnectionInformation =
      (PTDI_CONNECTION_INFORMATION)(pQuery + 1);
    pConnectionInformation = pQuery->RequestConnectionInformation;
    pConnectionInformation->UserDataLength = 0;
    pConnectionInformation->UserData = NULL;
    pConnectionInformation->OptionsLength = 0;
    pConnectionInformation->Options = NULL;
    pConnectionInformation->RemoteAddressLength = sizeof (TDI_ADDRESS_NETBIOS);
    pConnectionInformation->RemoteAddress =
      (PTA_NETBIOS_ADDRESS)(pConnectionInformation + 1);
    pRemoteAddress = pConnectionInformation->RemoteAddress;
    pRemoteAddress->TAAddressCount = 1;
    pRemoteAddress->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS);
    pRemoteAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
    pRemoteAddress->Address[0].Address[0].NetbiosNameType =
      TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
    UnicodeStringToAnsiString(
      pszIpxAddress,
      szIpxAddress,
      sizeof (szIpxAddress));
    RtlZeroMemory((PCHAR)&pRemoteAddress->Address[0].Address[0].NetbiosName, 10);
    StringToNodeNumber(
      (PCHAR)szIpxAddress,
      (PCHAR)&pRemoteAddress->Address[0].Address[0].NetbiosName[10]);
    TRACE6("IpxAddressToNetbiosName: Node=%02x:%02x:%02x:%02x:%02x:%02x\n",
      pRemoteAddress->Address[0].Address[0].NetbiosName[10],
      pRemoteAddress->Address[0].Address[0].NetbiosName[11],
      pRemoteAddress->Address[0].Address[0].NetbiosName[12],
      pRemoteAddress->Address[0].Address[0].NetbiosName[13],
      pRemoteAddress->Address[0].Address[0].NetbiosName[14],
      pRemoteAddress->Address[0].Address[0].NetbiosName[15]);

    dwBufferSize = 2048;
    for (;;) {
        pBuffer = LocalAlloc(LPTR, dwBufferSize);
        if (pBuffer == NULL) {
            TRACE("IpxAddressToNetbiosName: LocalAlloc failed");
            return NULL;
        }
        status = NtDeviceIoControlFile(
                   fd,
                   NULL,
                   NULL,
                   NULL,
                   &ioStatusBlock,
                   IOCTL_TDI_QUERY_INFORMATION,
                   pQuery,
                   dwQuerySize,
                   pBuffer,
                   dwBufferSize);
        if (status != STATUS_BUFFER_OVERFLOW)
            break;

        LocalFree(pBuffer);
        dwBufferSize *= 2;
        if (dwBufferSize >= 0xffff) {
            TRACE("IpxAddressToNetbiosName: Unable to allocate packet");
            return NULL;
        }
    }
    if (status == STATUS_PENDING) {
        status = NtWaitForSingleObject(fd, TRUE, NULL);
        if (status == STATUS_SUCCESS)
            status = ioStatusBlock.Status;
    }
    NtClose(fd);

    pAdapterStatus = (PADAPTERSTATUS)pBuffer;
    dwcNames = pAdapterStatus->AdapterInfo.name_count;
    TRACE2(
      "IpxAddressToNetbiosName: results (status=0x%x, dwcNames=%d)",
      status,
      dwcNames);
    if (status == STATUS_SUCCESS && dwcNames) {
        CHAR szNetbiosName[NCBNAMSZ + 1];

        pNames = pAdapterStatus->Names;
        if (CopyNetbiosName(pNames, dwcNames, szNetbiosName))
            pszNetbiosName = AnsiStringToUnicodeString(pNames->name, NULL, 0);
    }
    LocalFree(pBuffer);

    return pszNetbiosName;
} // IpxAddressToNetbiosName



BOOLEAN
NetbiosFindName(
    IN LPTSTR *pszDevices,
    IN DWORD dwcDevices,
    IN LPTSTR pszAddress
    )
{
    NTSTATUS *pStatus;
    PHANDLE pfd;
    PHANDLE pEvent;
    POBJECT_ATTRIBUTES pObjectAttributes;
    PIO_STATUS_BLOCK pIoStatusBlock;
    PUNICODE_STRING pUnicodeString;
    PTDI_REQUEST_QUERY_INFORMATION pQuery;
    PTDI_CONNECTION_INFORMATION pConnectionInformation;
    PTA_NETBIOS_ADDRESS pRemoteAddress;
    CHAR szAddress[NCBNAMSZ];
    PVOID *pBuffer;
    DWORD i, dwQuerySize, dwBufferSize;
    PADAPTERSTATUS pAdapterStatus;
    NAME_BUFFER *pNames;
    DWORD dwcWait, dwcNames;
    BOOLEAN bFound = FALSE;

    //
    // If there are no Netbios devices, then we're done.
    //
    if (pszDevices == NULL || !dwcDevices)
        return FALSE;
    //
    // Allocate our arrays up front.
    //
    pStatus = (NTSTATUS *)LocalAlloc(LPTR, dwcDevices * sizeof (NTSTATUS));
    if (pStatus == NULL) {
        TRACE("NetbiosFindName: LocalAlloc failed");
        return FALSE;
    }
    pfd = (PHANDLE)LocalAlloc(LPTR, dwcDevices * sizeof (HANDLE));
    if (pfd == NULL) {
        TRACE("NetbiosFindName: LocalAlloc failed");
        LocalFree(pStatus);
        return FALSE;
    }
    pUnicodeString = (PUNICODE_STRING)LocalAlloc(LPTR, dwcDevices * sizeof (UNICODE_STRING));
    if (pUnicodeString == NULL) {
        TRACE("NetbiosFindName: LocalAlloc failed");
        LocalFree(pStatus);
        LocalFree(pfd);
        return FALSE;
    }
    pEvent = (PHANDLE)LocalAlloc(
               LPTR,
               dwcDevices * sizeof (HANDLE));
    if (pEvent == NULL) {
        TRACE("NetbiosFindName: LocalAlloc failed");
        LocalFree(pStatus);
        LocalFree(pfd);
        LocalFree(pUnicodeString);
        return FALSE;
    }
    pObjectAttributes = (POBJECT_ATTRIBUTES)LocalAlloc(
                          LPTR,
                          dwcDevices * sizeof (OBJECT_ATTRIBUTES));
    if (pObjectAttributes == NULL) {
        TRACE("NetbiosFindName: LocalAlloc failed");
        LocalFree(pStatus);
        LocalFree(pfd);
        LocalFree(pUnicodeString);
        LocalFree(pEvent);
        return FALSE;
    }
    pIoStatusBlock = (PIO_STATUS_BLOCK)LocalAlloc(
                       LPTR,
                       dwcDevices * sizeof (IO_STATUS_BLOCK));
    if (pIoStatusBlock == NULL) {
        TRACE("NetbiosFindName: LocalAlloc failed");
        LocalFree(pStatus);
        LocalFree(pfd);
        LocalFree(pUnicodeString);
        LocalFree(pEvent);
        LocalFree(pObjectAttributes);
        return FALSE;
    }
    pBuffer = LocalAlloc(LPTR, dwcDevices * sizeof (PVOID));
    if (pBuffer == NULL) {
        TRACE("NetbiosFindName: LocalAlloc failed");
        LocalFree(pStatus);
        LocalFree(pfd);
        LocalFree(pUnicodeString);
        LocalFree(pEvent);
        LocalFree(pObjectAttributes);
        LocalFree(pIoStatusBlock);
        return FALSE;
    }
    //
    // Allocate and initialize our query structure.
    // We will give the same query to all the devices.
    //
    dwQuerySize = sizeof (TDI_REQUEST_QUERY_INFORMATION) +
                    sizeof (TDI_CONNECTION_INFORMATION) +
                    sizeof (TA_NETBIOS_ADDRESS);
    pQuery = LocalAlloc(LPTR, dwQuerySize);
    if (pQuery == NULL) {
        TRACE("NetbiosFindName: LocalAlloc failed");
        LocalFree(pStatus);
        LocalFree(pfd);
        LocalFree(pUnicodeString);
        LocalFree(pEvent);
        LocalFree(pObjectAttributes);
        LocalFree(pIoStatusBlock);
        return FALSE;
    }
    pQuery->QueryType = TDI_QUERY_ADAPTER_STATUS;
      (PTDI_CONNECTION_INFORMATION)&pQuery->RequestConnectionInformation;
    pQuery->RequestConnectionInformation =
      (PTDI_CONNECTION_INFORMATION)(pQuery + 1);
    pConnectionInformation = pQuery->RequestConnectionInformation;
    pConnectionInformation->UserDataLength = 0;
    pConnectionInformation->UserData = NULL;
    pConnectionInformation->OptionsLength = 0;
    pConnectionInformation->Options = NULL;
    pConnectionInformation->RemoteAddressLength = sizeof (TA_NETBIOS_ADDRESS);
    pConnectionInformation->RemoteAddress =
      (PTA_NETBIOS_ADDRESS)(pConnectionInformation + 1);
    pRemoteAddress = pConnectionInformation->RemoteAddress;
    pRemoteAddress->TAAddressCount = 1;
    pRemoteAddress->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS);
    pRemoteAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
    pRemoteAddress->Address[0].Address[0].NetbiosNameType =
      TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
    UnicodeStringToAnsiString(
      pszAddress,
      szAddress,
      sizeof (szAddress));
    RtlFillMemory(
      (PCHAR)&pRemoteAddress->Address[0].Address[0].NetbiosName,
      NCBNAMSZ,
      ' ');
    //
    // Make sure the Netbios name is in uppercase!
    //
    _strupr(szAddress);
    RtlCopyMemory(
      (PCHAR)&pRemoteAddress->Address[0].Address[0].NetbiosName,
      szAddress,
      strlen(szAddress));
    pRemoteAddress->Address[0].Address[0].NetbiosName[NCBNAMSZ - 1] = '\0';
    TRACE1("NetbiosFindName: address=%s", szAddress);
    //
    // Initialize the OBJECT_ATTRIBUTES structure,
    // open the device, and start the query
    // for each device.
    //
    for (i = 0; i < dwcDevices; i++) {
        pBuffer[i] = NULL;

        RtlInitUnicodeString(&pUnicodeString[i], pszDevices[i]);
        InitializeObjectAttributes(
          &pObjectAttributes[i],
          &pUnicodeString[i],
          OBJ_CASE_INSENSITIVE,
          NULL,
          NULL);
        pEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
        if (pEvent[i] == NULL) {
            TRACE("NetbiosFindName: CreateEvent failed");
            goto done;
        }
        pStatus[i] = NtCreateFile(
                       &pfd[i],
                       FILE_READ_DATA|FILE_WRITE_DATA,
                       &pObjectAttributes[i],
                       &pIoStatusBlock[i],
                       NULL,
                       FILE_ATTRIBUTE_NORMAL,
                       FILE_SHARE_READ|FILE_SHARE_WRITE,
                       FILE_OPEN_IF,
                       0,
                       NULL,
                       0);
        if (!NT_SUCCESS(pStatus[i])) {
            TRACE1("NetbiosFindName: NtCreateFile failed (status=0x%x)", pStatus[i]);
            continue;
        }
        //
        // Allocate the results buffer.
        //
        dwBufferSize = 2048;
        for (;;) {
            pBuffer[i] = LocalAlloc(LPTR, dwBufferSize);
            if (pBuffer[i] == NULL) {
                TRACE("NetbiosFindName: LocalAlloc failed");
                goto done;
            }
            pStatus[i] = NtDeviceIoControlFile(
                           pfd[i],
                           pEvent[i],
                           NULL,
                           NULL,
                           &pIoStatusBlock[i],
                           IOCTL_TDI_QUERY_INFORMATION,
                           pQuery,
                           dwQuerySize,
                           pBuffer[i],
                           dwBufferSize);
            if (pStatus[i] != STATUS_BUFFER_OVERFLOW)
                break;

            LocalFree(pBuffer[i]);
            pBuffer[i] = NULL;
            dwBufferSize *= 2;
            if (dwBufferSize >= 0xffff) {
                TRACE("NetbiosFindName: Unable to allocate packet");
                break;
            }
        }
    }
    //
    // Determine whether any of the
    // requests returned STATUS_SUCCESS.
    //
    TRACE("NetbiosFindName: checking for STATUS_SUCCESS");
    dwcWait = 0;
    for (i = 0; i < dwcDevices; i++) {
        TRACE2("NetbiosFindName: %S: status=%d", pszDevices[i], pStatus[i]);
        if (pStatus[i] == STATUS_SUCCESS) {
            pAdapterStatus = (PADAPTERSTATUS)pBuffer[i];
            dwcNames = pAdapterStatus->AdapterInfo.name_count;
            TRACE2(
              "NetbiosFindName: %S: dwcNames=%d",
              pszDevices[i],
              dwcNames);
            if (dwcNames) {
                bFound = TRUE;
                goto done;
            }
        }
        else if (pStatus[i] == STATUS_PENDING)
            dwcWait++;
    }
    //
    // If we didn't find a successful return,
    // then wait for the others to complete.
    //
    TRACE1("NetbiosFindName: dwcWait=%d", dwcWait);
    for (i = 0; i < dwcWait; i++) {
        NTSTATUS status;
        DWORD dwiDevice;

        status = WaitForMultipleObjects(dwcDevices, pEvent, FALSE, INFINITE);
        TRACE1("NetbiosFindName: WaitForMultipleObjects returned 0x%x", status);
        if (status == WAIT_FAILED) {
            TRACE1(
              "NetbiosFindName: WaitForMultipleObjects failed (status=0x%x)",
              GetLastError());
            goto done;
        }
        dwiDevice = (DWORD)status - WAIT_OBJECT_0;
        if (dwiDevice >= dwcDevices) {
            TRACE(
              "NetbiosFindName: WaitForMultipleObjects returned STATUS_ABANDONED?");
            goto done;
        }
        pStatus[dwiDevice] = pIoStatusBlock[dwiDevice].Status;
        TRACE2(
          "NetbiosFindName: %S returned status 0x%x from wait",
          pszDevices[dwiDevice],
          pStatus[dwiDevice]);
        if (pStatus[dwiDevice] == STATUS_SUCCESS) {
            pAdapterStatus = (PADAPTERSTATUS)pBuffer[dwiDevice];
            dwcNames = pAdapterStatus->AdapterInfo.name_count;
            TRACE2(
              "NetbiosFindName: %S: dwcNames=%d",
              pszDevices[dwiDevice],
              dwcNames);
            if (dwcNames) {
                bFound = TRUE;
                goto done;
            }
        }
    }
done:
    //
    // Free the resources associated with
    // each device.
    //
    for (i = 0; i < dwcDevices; i++) {
        TRACE4(
          "NetbiosFindName: pIoStatusBlock[%d]=0x%x, pBuffer[%d]=0x%x",
          i,
          &pIoStatusBlock[i],
          i,
          pBuffer[i]);
        (void)NtCancelIoFile(pfd[i], &pIoStatusBlock[i]);
        if (pfd[i] != NULL)
            NtClose(pfd[i]);
        if (pEvent[i] != NULL)
            CloseHandle(pEvent[i]);
        if (pBuffer[i] != NULL)
            LocalFree(pBuffer[i]);
    }
    //
    // Free the buffers we allocated above.
    //
    LocalFree(pStatus);
    LocalFree(pfd);
    LocalFree(pUnicodeString);
    LocalFree(pEvent);
    LocalFree(pObjectAttributes);
    LocalFree(pIoStatusBlock);
    LocalFree(pBuffer);

    return bFound;
} // NetbiosFindName



struct hostent *
IpAddressToHostent(
    IN LPTSTR pszInetAddress
    )
{
    CHAR szInetAddress[ACD_ADDR_INET_LEN];
    ULONG inaddr;
    struct hostent *hp;

    UnicodeStringToAnsiString(
      pszInetAddress,
      szInetAddress,
      sizeof (szInetAddress));
    inaddr = inet_addr(szInetAddress);
    //
    // Disable the address so when we call gethostbyname(),
    // we won't cause an autodial attempt.  Enable it after
    // we're done.
    //
    SetAddressDisabled(pszInetAddress, TRUE);
    hp = gethostbyaddr((char *)&inaddr, 4, PF_INET);
    SetAddressDisabled(pszInetAddress, FALSE);

    return hp;
} // InetAddressToHostent



struct hostent *
InetAddressToHostent(
    IN LPTSTR pszInetAddress
    )
{
    CHAR szInetAddress[ACD_ADDR_INET_LEN];
    struct hostent *hp;

    UnicodeStringToAnsiString(
      pszInetAddress,
      szInetAddress,
      sizeof (szInetAddress));
    //
    // Disable the address so when we call gethostbyname(),
    // we won't cause an autodial attempt.  Enable it after
    // we're done.
    //
    SetAddressDisabled(pszInetAddress, TRUE);
    hp = gethostbyname(szInetAddress);
    SetAddressDisabled(pszInetAddress, FALSE);

    return hp;
} // InetAddressToHostEnt



BOOLEAN
PingIpAddress(
    IN LPTSTR pszIpAddress
    )

/*++

DESCRIPTION
    Determine whether an IP address is accessible by pinging it.

ARGUMENTS
    lpszAddress: the IP address

RETURN VALUE
    TRUE if lpszAddress is accessible, FALSE otherwise.

--*/

{
    BOOLEAN fSuccess = FALSE;
    LONG inaddr;
    char szIpAddress[17];
    int i, nReplies, nTry;
    char *lpSendBuf = NULL, *lpRecvBuf = NULL;
    HANDLE hIcmp = NULL;
    IP_OPTION_INFORMATION SendOpts;
    PICMP_ECHO_REPLY lpReply;

    UnicodeStringToAnsiString(pszIpAddress, szIpAddress, sizeof (szIpAddress));
    inaddr = inet_addr(szIpAddress);
    TRACE2("PingIpAddress: IP address=(%s, 0x%x)", szIpAddress, inaddr);
    //
    // Check to make sure we loaded icmp.dll.
    //
    if (hIcmpG == NULL) {
        TRACE("PingIpAddress: icmp.dll not loaded!");
        return FALSE;
    }
    //
    // Open the icmp device.
    //
    hIcmp = (HANDLE)(*lpfnIcmpCreateFileG)();
    if (hIcmp == INVALID_HANDLE_VALUE) {
        TRACE("PingIpAddress: IcmpCreateFile failed");
        return FALSE;
    }
    //
    // Allocate the send and receive buffers.
    //
    lpSendBuf = LocalAlloc(LMEM_FIXED, PING_SEND_SIZE);
    if (lpSendBuf == NULL) {
        TRACE("PingIpAddress: LocalAlloc failed");
        goto done;
    }
    lpRecvBuf = LocalAlloc(LMEM_FIXED, PING_RECV_SIZE);
    if (lpRecvBuf == NULL) {
        TRACE("PingIpAddress: LocalAlloc failed");
        goto done;
    }
    //
    // Initialize the send buffer pattern.
    //
    for (i = 0; i < PING_SEND_SIZE; i++)
        lpSendBuf[i] = 'a' + (i % 23);
    //
    // Initialize the send options.
    //
    SendOpts.OptionsData = (unsigned char FAR *)0;
    SendOpts.OptionsSize = 0;
    SendOpts.Ttl = PING_TTL;
    SendOpts.Tos = 0;
    SendOpts.Flags = 0;
    //
    // Ping the host.
    //
    for (nTry = 0; nTry < 3; nTry++) {
        DWORD dwTimeout = 750;

#ifdef notdef
        if (nTry < 2)
            dwTimeout = 750;
        else
            dwTimeout = 2000;
#endif
        //
        // Check to make sure the service isn't shutting
        // down before we start on our next iteration.
        //
        if (WaitForSingleObject(hTerminatingG, 0) != WAIT_TIMEOUT) {
            TRACE("PingIpAddress: shutting down");
            LocalFree(lpRecvBuf);
            LocalFree(lpSendBuf);
            return FALSE;
        }
        nReplies = (*lpfnIcmpSendEchoG)(
                     hIcmp,
                     inaddr,
                     lpSendBuf,
                     (unsigned short)PING_SEND_SIZE,
                     &SendOpts,
                     lpRecvBuf,
                     PING_RECV_SIZE,
                     dwTimeout);
        //
        // Look at the responses to see
        // if any are successful.
        //
        for (lpReply = (PICMP_ECHO_REPLY)lpRecvBuf, i = 0;
             i < nReplies;
             lpReply++, i++)
        {
            TRACE2(
              "PingIpAddress: ping reply status[%d]=%d",
              i,
              lpReply->Status);
            //
            // Unless the status is IP_REQ_TIMED_OUT,
            // we're done.
            //
            fSuccess = (lpReply->Status == IP_SUCCESS);
            if (lpReply->Status != IP_REQ_TIMED_OUT)
                goto done;
        }
    }
    //
    // Clean up.
    //
done:
    if (lpRecvBuf != NULL)
        LocalFree(lpRecvBuf);
    if (lpSendBuf != NULL)
        LocalFree(lpSendBuf);
    if (hIcmp != NULL)
        (*lpfnIcmpCloseHandleG)(hIcmp);

    return fSuccess;
} // PingIpAddress



VOID
LoadIcmpDll(VOID)
{
    hIcmpG = LoadLibrary(ICMP_MODULE);
    if (hIcmpG == NULL)
        return;
    lpfnIcmpCreateFileG = GetProcAddress(hIcmpG, ICMPCREATEFILE);
    lpfnIcmpSendEchoG = GetProcAddress(hIcmpG, ICMPSENDECHO);
    lpfnIcmpCloseHandleG = GetProcAddress(hIcmpG, ICMPCLOSEHANDLE);
    if (lpfnIcmpCreateFileG == NULL ||
        lpfnIcmpSendEchoG == NULL ||
        lpfnIcmpCloseHandleG == NULL)
    {
        FreeLibrary(hIcmpG);
        hIcmpG = NULL;
        return;
    }
} // LoadIcmpDll



VOID
UnloadIcmpDll(VOID)
{
    if (hIcmpG != NULL) {
        FreeLibrary(hIcmpG);
        hIcmpG = NULL;
    }
}
