windows-nt/Source/XPSP1/NT/net/netbt/lmhosts/lmhsvc.c

1740 lines
40 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1992,1993 Microsoft Corporation
Module Name:
lmhsvc.c
Abstract:
This module implements the LmHosts Service, which is part of the LmSvc
process.
One purpose of the LmHosts Service is to send down a NBT_RESYNC
ioctl command to netbt.sys, after the LanmanWorkstation service has been
started. To accomplish this, the NT Registry is primed so that the
LmHosts service is dependent on the LanmanWorkStation service.
This service also handle name query requests from netbt destined for
DNS by way of gethostbyname.
Author:
Jim Stewart November 18 22, 1993
Revision History:
ArnoldM 14-May-1996 Use winsock2 name resolution
instead of gethostbyname
Notes:
--*/
//
// Standard NT Headers
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
//
// C Runtime Library Headers
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
//
// Transport Specific Header Files
//
#include <nbtioctl.h>
//
// Standard Windows Headers
//
#include <windows.h>
#include <tdi.h>
//
// LAN Manager Headers
//
#include <lm.h>
#include <netlib.h>
#include <netevent.h>
//
// Sockets Headers
//
#include <winsock2.h>
#include <svcguid.h>
#include <wsahelp.h>
//
// Private Definitions
//
#define NBT_DEVICE "\\Device\\Streams\\Nbt"
#define WSA_QUERY_BUFFER_LENGTH (3*1024)
BYTE pWSABuffer[WSA_QUERY_BUFFER_LENGTH];
//
// We currently have two threads; one for DNS names, the other for checking IP addresses
// for reachability.
//
#define NUM_THREADS 2
//
// Function Prototypes
//
VOID
announceStatus (
IN LPSERVICE_STATUS svcstatus
);
VOID
lmhostsHandler (
IN DWORD opcode
);
VOID
lmhostsSvc (
IN DWORD argc,
IN LPTSTR *argv
);
NTSTATUS
prepareData (
IN DWORD argc,
IN LPTSTR argv[]
);
LONG
DeviceIoCtrl(
IN HANDLE fd,
IN PVOID ReturnBuffer,
IN ULONG BufferSize,
IN ULONG Ioctl,
IN ULONG i
);
LONG
PrimeCacheNbt(
OUT PHANDLE pHandle,
IN ULONG index
);
NTSTATUS
Resync(
IN HANDLE fd
);
LONG
ReadRegistry(
IN WCHAR *pDeviceName[]
);
NTSTATUS
OpenNbt(
IN WCHAR *path[],
OUT PHANDLE pHandle
);
LONG
GetHostName(
IN HANDLE fd,
IN tIPADDR_BUFFER_DNS *pIpAddrBuffer
);
LONG
PostForGetHostByName(
IN HANDLE fd
);
VOID
CheckIPAddrWorkerRtn(
IN LPVOID lpParam
);
LONG
CheckIPAddresses(
IN tIPADDR_BUFFER_DNS *pIpAddrBuffer,
IN ULONG *IpAddr,
IN BOOLEAN fOrdered
);
//
// Global Variables
//
PUCHAR EventSource = "LmHostsService";
HANDLE Poison[NUM_THREADS]; // set to kill this service
HANDLE NbtEvent[NUM_THREADS]; // set when Nbt returns the Irp
SERVICE_STATUS_HANDLE SvcHandle;
SERVICE_STATUS SvcStatus;
BOOLEAN Trace = FALSE; // TRUE for debugging
tIPADDR_BUFFER_DNS *pIpAddrBuffer;
tIPADDR_BUFFER_DNS *pIpAddrBufferChkIP;
BOOLEAN SocketsUp;
#if DBG
#define DBG_PRINT DbgPrint
#else
#define DBG_PRINT
#endif // DBG
//------------------------------------------------------------------------
VOID
ServiceMain (
IN DWORD argc,
IN LPTSTR *argv
)
/*++
Routine Description:
This is the SERVICE_MAIN_FUNCTION.
Arguments:
argc, argv
Return Value:
None.
--*/
{
DWORD status;
HANDLE hNbt;
HANDLE EventList[2];
ULONG EventCount;
LONG err;
WSADATA WsaData;
LONG Value;
HANDLE hThread;
ULONG Tid;
ULONG *pIndex;
LARGE_INTEGER Timeout = RtlEnlargedIntegerMultiply (-10 * 60, 1000 * 1000 * 10); // 10 minutes
if (STATUS_SUCCESS != (prepareData(argc, argv)))
{
ExitThread(NO_ERROR);
}
pIpAddrBuffer = NULL;
pIpAddrBufferChkIP = NULL;
if (Trace)
{
DbgPrint("LMHSVC: calling RegisterServiceCtrlHandler()\n");
}
SvcHandle = RegisterServiceCtrlHandler(SERVICE_LMHOSTS, // ServiceName
lmhostsHandler); // HandlerProc
if (SvcHandle == (SERVICE_STATUS_HANDLE) 0)
{
DBG_PRINT ("LMHSVC: RegisterServiceCtrlHandler failed %d\n", GetLastError());
ExitThread(NO_ERROR);
return;
}
SvcStatus.dwServiceType = SERVICE_WIN32;
SvcStatus.dwCurrentState = SERVICE_START_PENDING;
SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
SvcStatus.dwWin32ExitCode = 0;
SvcStatus.dwServiceSpecificExitCode = 0;
SvcStatus.dwCheckPoint = 0;
SvcStatus.dwWaitHint = 20000; // 20 seconds
SET_SERVICE_EXITCODE(NO_ERROR, // SomeApiStatus
SvcStatus.dwWin32ExitCode, // Win32CodeVariable
SvcStatus.dwServiceSpecificExitCode); // NetCodeVariable
announceStatus(&SvcStatus);
SvcStatus.dwCurrentState = SERVICE_RUNNING;
SvcStatus.dwCheckPoint = 0;
SvcStatus.dwWaitHint = 0;
announceStatus(&SvcStatus);
//
// startup the sockets interface
//
err = WSAStartup( 0x0101, &WsaData );
if ( err == SOCKET_ERROR )
{
//s_perror("nbtstat: WSAStartup:", GetLastError());
#if 0
NetdLogEventA(
EventSource, // EventSource
EVENT_NBTSVC_LMHOST_SYNC_FAIL, // MessageId
EVENTLOG_WARNING_TYPE, // EventType
0, // NumberofSubStrings
NULL); // SubStrings
#endif
SocketsUp = FALSE;
}
else
{
SocketsUp = TRUE;
}
if ((pIndex = LocalAlloc(LMEM_FIXED, sizeof(ULONG))) == NULL)
{
DBG_PRINT ("LMHSVC: malloc of index failed %d\n", GetLastError());
ExitThread(NO_ERROR);
return;
}
if (Trace)
{
DbgPrint("LMHSVC: CreateThread attempting...\n");
}
*pIndex = 1;
hThread = CreateThread (NULL, // lpThreadAttributes
0, // dwStackSize
(LPTHREAD_START_ROUTINE) CheckIPAddrWorkerRtn, // lpStartAddress
&pIndex, // lpParameter
0, // dwCreationFlags
&Tid); // lpThreadId
if (hThread == NULL)
{
DBG_PRINT ("LMHSVC: CreateThread failed %d\n", GetLastError());
ExitThread(NO_ERROR);
return;
}
//
// ignore the return code from resyncNbt().
//
// In most cases (no domains spanning an ip router), it is not a
// catastrophe if nbt.sys couldn't successfully process the NBT_RESYNC
// ioctl command. Since I'm ignoring the return, I announce I'm running
// before I call it to allow other dependent services to start.
//
//
status = PrimeCacheNbt(&hNbt, 0);
if (Trace)
{
DbgPrint("LMHSVC: Thread 0, hNbt %lx\n", hNbt);
}
if (hNbt != (HANDLE)-1)
{
status = PostForGetHostByName(hNbt);
if (status == NO_ERROR)
{
EventCount = 2;
}
else
{
if (Trace)
{
DbgPrint("Lmhsvc: Error posting Irp for get host by name\n");
}
EventCount = 1;
}
}
else
{
EventCount = 1;
}
//
// "A SERVICE_MAIN_FUNCTION does not return until the service is ready
// to terminate."
//
// (ref: api32wh.hlp, SERVICE_MAIN_FUNCTION)
//
//
assert(Poison[0]);
EventList[0] = Poison[0];
EventList[1] = NbtEvent[0];
while (TRUE)
{
status = NtWaitForMultipleObjects(EventCount,
EventList,
WaitAny, // wait for any event
FALSE,
(EventCount == 1)? &Timeout: NULL);
if (status == WAIT_TIMEOUT)
{
if (hNbt == (HANDLE)-1)
{
PrimeCacheNbt(&hNbt, 0);
if (hNbt == (HANDLE)-1)
{
continue; // to wait
}
}
status = PostForGetHostByName(hNbt); // try again
if (status == NO_ERROR)
{
EventCount = 2;
}
}
else if (status == 1)
{
if (Trace)
{
DbgPrint("LMHSVC: Doing GetHostName\n");
}
// the irp used for gethostby name has returned
status = GetHostName(hNbt,pIpAddrBuffer);
//
// disable the get host by name stuff if we have an error
// posting a buffer to the transport
//
if (status != NO_ERROR)
{
EventCount = 1;
}
}
else
{
// it must have been a the Poison event signalling the end of the
// the service, so exit after getting the Irp back from the
// transport. This system will look after canceling the IO and
// getting the Irp back.
NtClose(Poison[0]);
NtClose(NbtEvent[0]);
NtClose(hNbt);
break;
}
}
if (pIpAddrBuffer)
{
LocalFree (pIpAddrBuffer);
}
if (pIpAddrBufferChkIP)
{
LocalFree (pIpAddrBufferChkIP);
}
if (Trace)
{
DBG_PRINT ("LMHSVC: [LMSVCS_ENTRY_POINT] Exiting now!\n");
}
ExitThread(NO_ERROR);
return;
} // lmhostsSvc
//------------------------------------------------------------------------
VOID
announceStatus (
IN LPSERVICE_STATUS status
)
/*++
Routine Description:
This procedure announces the service's status to the service controller.
Arguments:
None.
Return Value:
None.
--*/
{
if (!SvcHandle) {
return;
}
#if DBG
if (Trace)
{
DbgPrint( "LMHSVC: announceStatus:\n"
" CurrentState %lx\n"
" ControlsAccepted %lx\n"
" Win32ExitCode %lu\n"
" ServiceSpecificExitCode %lu\n"
" CheckPoint %lu\n"
" WaitHint %lu\n",
status->dwCurrentState,
status->dwControlsAccepted,
status->dwWin32ExitCode,
status->dwServiceSpecificExitCode,
status->dwCheckPoint,
status->dwWaitHint);
}
#endif // DBG
SetServiceStatus(SvcHandle, status);
} // announceStatus
//------------------------------------------------------------------------
VOID
lmhostsHandler (
IN DWORD controlcode
)
/*++
Routine Description:
This is the HANDLER_FUNCTION of the LmHosts service.
It only responds to two Service Controller directives: to stop, and
to announce the current status of the service.
Arguments:
opcode
Return Value:
None.
--*/
{
BOOL retval;
ULONG i;
switch (controlcode) {
case SERVICE_CONTROL_STOP:
for (i=0; i<NUM_THREADS; i++) {
assert(Poison[i]);
retval = PulseEvent(Poison[i]);
assert(retval);
}
SvcStatus.dwCurrentState = SERVICE_STOPPED;
SvcStatus.dwCheckPoint = 0;
SvcStatus.dwWaitHint = 0;
/* fall through */
case SERVICE_CONTROL_INTERROGATE:
announceStatus(&SvcStatus);
break;
case SERVICE_CONTROL_CONTINUE:
case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_SHUTDOWN:
default:
assert(0);
break;
}
} // lmhostsHandler
//------------------------------------------------------------------------
NTSTATUS
prepareData (
IN DWORD argc,
IN LPTSTR argv[]
)
/*++
Routine Description:
Arguments:
argc, argv
Return Value:
None.
--*/
{
DWORD i;
DWORD status;
for (i=0; i<NUM_THREADS; i++)
{
Poison[i] = CreateEvent(NULL, // security attributes
FALSE, // ManualReset
FALSE, // InitialState
NULL); // EventName
if (!Poison[i])
{
DBG_PRINT ("LMHSVC: couldn't CreateEvent()\n");
return (STATUS_INSUFFICIENT_RESOURCES);
}
NbtEvent[i] = CreateEvent(NULL, // security attributes
FALSE, // ManualReset
FALSE, // InitialState
NULL); // EventName
if (!NbtEvent[i])
{
DBG_PRINT ("LMHSVC: couldn't CreateEvent()\n");
return (STATUS_INSUFFICIENT_RESOURCES);
}
}
return STATUS_SUCCESS;
} // prepareData
//------------------------------------------------------------------------
LONG
DeviceIoCtrl(
IN HANDLE fd,
IN PVOID ReturnBuffer,
IN ULONG BufferSize,
IN ULONG Ioctl,
IN ULONG index
)
/*++
Routine Description:
This procedure performs an ioctl(I_STR) on a stream.
Arguments:
fd - NT file handle
iocp - pointer to a strioctl structure
Return Value:
0 if successful, -1 otherwise.
--*/
{
NTSTATUS status;
int retval;
ULONG QueryType;
TDI_REQUEST_QUERY_INFORMATION QueryInfo;
IO_STATUS_BLOCK iosb;
PVOID pInput;
ULONG SizeInput;
pInput = NULL;
SizeInput = 0;
status = NtDeviceIoControlFile(
fd, // Handle
NbtEvent[index], // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb, // IoStatusBlock
Ioctl, // IoControlCode
pInput, // InputBuffer
SizeInput, // InputBufferSize
(PVOID) ReturnBuffer, // OutputBuffer
BufferSize); // OutputBufferSize
if (status == STATUS_PENDING)
{
// do not wait for this to complete since it could complete
// at any time in the future.
//
if ((Ioctl == IOCTL_NETBT_DNS_NAME_RESOLVE) ||
(Ioctl == IOCTL_NETBT_CHECK_IP_ADDR))
{
return(NO_ERROR);
}
else
{
status = NtWaitForSingleObject(
fd, // Handle
TRUE, // Alertable
NULL); // Timeout
}
}
if (NT_SUCCESS(status))
{
return(NO_ERROR);
}
else
return(ERROR_FILE_NOT_FOUND);
}
//------------------------------------------------------------------------
NTSTATUS
Resync(
IN HANDLE fd
)
/*++
Routine Description:
This procedure tells NBT to purge all names from its remote hash
table cache.
Arguments:
Return Value:
0 if successful, -1 otherwise.
--*/
{
NTSTATUS status;
CHAR Buffer;
status = DeviceIoCtrl(fd,
&Buffer,
1,
IOCTL_NETBT_PURGE_CACHE,
0); // pass 0 since we know that we are called only for the first thread
return(status);
}
#if 0
//------------------------------------------------------------------------
PCHAR
GetHost(ULONG addr,BOOLEAN Convert)
{
static char string[32];
union inet
{
unsigned long l;
char c[4];
}in;
struct hostent *hp;
int i;
if (addr == 0L)
return(" ");
/*
* Look up the address in the in-core host table.
* If it's there, that'll do the trick.
*/
if (Convert)
{
if ( hp = gethostbyname((char *) &addr,sizeof(addr),2) )
{
return( hp->h_name );
}
}
in.l = addr;
sprintf(string, "%u.%u.%u.%u", (unsigned char) in.c[0],
(unsigned char) in.c[1], (unsigned char) in.c[2],
(unsigned char) in.c[3]);
return(string);
}
#endif
//------------------------------------------------------------------------
NTSTATUS
PrimeCacheNbt(
OUT PHANDLE pHandle,
IN ULONG index
)
/*++
Routine Description:
This procedure sends a NBT_PURGE ioctl command down to netbt.sys.
Arguments:
None.
Return Value:
0 if successful, an error code otherwise.
--*/
{
LONG status;
HANDLE Handle;
int i;
//
// In PnP, the Netbt device is not created until the first IP address has
// appeared on that adapter. We need to open all available devices until
// an open succeeds.
//
WCHAR *DeviceName[NBT_MAXIMUM_BINDINGS];
UCHAR stringbuf[24];
UCHAR *subStrings[1];
*pHandle = (HANDLE)-1;
for (i=0; i<NBT_MAXIMUM_BINDINGS; i++)
{
DeviceName[i] = NULL;
}
#ifdef USE_REGISTRY
status = ReadRegistry(DeviceName);
if (status == NO_ERROR)
{
status = OpenNbt(DeviceName,&Handle);
#else
{
PWCHAR ExportDevice[ ] = { L"\\Device\\NetBt_Wins_Export", 0 };
status = OpenNbt(ExportDevice,&Handle);
#endif
if (status == NO_ERROR)
{
//
// Resync only once...
//
if (index == 0) {
Resync(Handle);
}
*pHandle = Handle;
}
}
for (i=0; i<NBT_MAXIMUM_BINDINGS; i++)
{
if (DeviceName[i])
{
free (DeviceName[i]);
}
else
{
break;
}
}
if (status != NO_ERROR)
{
sprintf(stringbuf, "%lu", status);
subStrings[0] = stringbuf;
#if 0
NetdLogEventA(
EventSource, // EventSource
EVENT_NBTSVC_LMHOST_SYNC_FAIL, // MessageId
EVENTLOG_WARNING_TYPE, // EventType
1, // NumberofSubStrings
(UCHAR **) subStrings); // SubStrings
#endif
}
return(status);
}
//------------------------------------------------------------------------
LONG
ReadRegistry(
IN WCHAR *pDeviceName[]
)
/*++
Routine Description:
This procedure reads the registry to get the name of NBT to bind to.
That name is stored in the Linkage/Exports section under the Netbt key.
Arguments:
Return Value:
0 if successful, -1 otherwise.
--*/
{
PWCHAR SubKeyLinkage=L"system\\currentcontrolset\\services\\netbt\\linkage";
HKEY Key;
PWCHAR Linkage=L"Export";
LONG Type;
UCHAR *pBuffer;
LONG status;
LONG status2;
ULONG size = 512;
pBuffer = malloc(size);
if (!pBuffer)
{
return (STATUS_INSUFFICIENT_RESOURCES);
}
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
SubKeyLinkage,
0,
KEY_READ,
&Key);
if (status == ERROR_SUCCESS)
{
// now read the linkage values
while (pBuffer && ((status = RegQueryValueEx( Key,
Linkage,
NULL,
&Type,
pBuffer,
&size)) == ERROR_MORE_DATA))
{
// Get a buffer that is big enough.
size += 512;
pBuffer = realloc( pBuffer, size );
}
if (status == ERROR_SUCCESS)
{
PUCHAR curPtr=pBuffer;
LONG index=0;
//
// Copy over all the export keys
//
while((*curPtr) && (index < NBT_MAXIMUM_BINDINGS))
{
if (!(pDeviceName[index] = malloc (sizeof(WCHAR) * (wcslen((PWCHAR)curPtr) + 1))))
{
break;
}
wcscpy(pDeviceName[index],(PWCHAR)curPtr);
++index;
curPtr += sizeof(WCHAR) * (wcslen((PWCHAR)curPtr) + 1);
}
}
status2 = RegCloseKey(Key);
}
if (pBuffer)
{
free (pBuffer);
}
return(status);
}
//------------------------------------------------------------------------
NTSTATUS
OpenNbt(
IN WCHAR *path[],
OUT PHANDLE pHandle
)
/*++
Routine Description:
This function opens a stream.
Arguments:
path - path to the STREAMS driver
oflag - currently ignored. In the future, O_NONBLOCK will be
relevant.
ignored - not used
Return Value:
An NT handle for the stream, or INVALID_HANDLE_VALUE if unsuccessful.
--*/
{
HANDLE StreamHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
STRING name_string;
UNICODE_STRING uc_name_string;
NTSTATUS status;
LONG index=0;
while ((path[index]) && (index < NBT_MAXIMUM_BINDINGS))
{
RtlInitUnicodeString(&uc_name_string,path[index]);
InitializeObjectAttributes(
&ObjectAttributes,
&uc_name_string,
OBJ_CASE_INSENSITIVE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL
);
status =
NtCreateFile(
&StreamHandle,
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))
{
*pHandle = StreamHandle;
return(NO_ERROR);
}
++index;
}
return(ERROR_FILE_NOT_FOUND);
}
//------------------------------------------------------------------------
LONG
PostForGetHostByName(
IN HANDLE fd
)
/*++
Routine Description:
This procedure passes a buffer down to Netbt for it to return when it
wants a name resolved via DNS.
Arguments:
Return Value:
0 if successful, -1 otherwise.
--*/
{
LONG status = ERROR_FILE_NOT_FOUND;
CHAR Buffer;
if (!pIpAddrBuffer)
{
if (!(pIpAddrBuffer = LocalAlloc(LMEM_FIXED,sizeof(tIPADDR_BUFFER_DNS))))
{
DBG_PRINT ("lmhsvc: failed to send irp due to error = %x\n",status);
return(ERROR_NOT_ENOUGH_MEMORY);
}
pIpAddrBuffer->Resolved = FALSE;
pIpAddrBuffer->IpAddrsList[0] = 0; // for DBG
}
status = DeviceIoCtrl (fd,
pIpAddrBuffer,
sizeof(tIPADDR_BUFFER_DNS),
IOCTL_NETBT_DNS_NAME_RESOLVE,
0); // hard coded thread Index
return(status);
}
LONG
PostForCheckIPAddr(
IN HANDLE fd
)
/*++
Routine Description:
This procedure passes a buffer down to Netbt for it to return when it
wants a name resolved via DNS.
Arguments:
Return Value:
0 if successful, -1 otherwise.
--*/
{
LONG status = ERROR_FILE_NOT_FOUND;
CHAR Buffer;
if (!pIpAddrBufferChkIP)
{
if (!(pIpAddrBufferChkIP = LocalAlloc(LMEM_FIXED,sizeof(tIPADDR_BUFFER_DNS))))
{
DBG_PRINT ("lmhsvc: failed to send irp due to error = %x\n",status);
return(ERROR_NOT_ENOUGH_MEMORY);
}
pIpAddrBufferChkIP->Resolved = FALSE;
pIpAddrBufferChkIP->IpAddrsList[0] = 0; // for DBG
}
status = DeviceIoCtrl (fd,
pIpAddrBufferChkIP,
sizeof(tIPADDR_BUFFER_DNS),
IOCTL_NETBT_CHECK_IP_ADDR,
1); // hard coded thread Index
if (Trace)
{
DbgPrint("LMHSVC: Entered PostForCheckIPAddr. status: %lx\n", status);
}
return(status);
}
GUID HostnameGuid = SVCID_INET_HOSTADDRBYNAME;
VOID
GetHostNameCopyBack(
tIPADDR_BUFFER_DNS *pIpAddrBuffer,
PWSAQUERYSETW pwsaq
)
{
//
// success, fetch the CSADDR structure
//
PCSADDR_INFO pcsadr;
ULONG GoodAddr;
NTSTATUS Status;
int i = 0;
int imax = min(MAX_IPADDRS_PER_HOST, pwsaq->dwNumberOfCsAddrs);
pcsadr = pwsaq->lpcsaBuffer;
if (pwsaq->lpszServiceInstanceName) {
wcsncpy(pIpAddrBuffer->pwName, pwsaq->lpszServiceInstanceName, DNS_NAME_BUFFER_LENGTH);
pIpAddrBuffer->pwName[DNS_NAME_BUFFER_LENGTH-1] = 0;
pIpAddrBuffer->NameLen = wcslen(pIpAddrBuffer->pwName) * sizeof(WCHAR);
if (Trace) {
DbgPrint("Lmhsvc: Resolved name = \"%ws\"\n", pIpAddrBuffer->pwName);
}
}
if (pIpAddrBuffer->Resolved) {
/* In this case, we have been called before. No need to copy the IPs back again. */
/* But we do need to copy the name back since it is the alias name that KsecDD requires */
return;
}
for(i=0; i<imax; i++, pcsadr++)
{
PSOCKADDR_IN sockaddr;
sockaddr = (PSOCKADDR_IN)pcsadr->RemoteAddr.lpSockaddr;
pIpAddrBuffer->IpAddrsList[i] = htonl( sockaddr->sin_addr.s_addr);
if (Trace)
{
DbgPrint("LmhSvc: Dns IpAddrsList[%d/%d] =%x\n",
(i+1),imax,pIpAddrBuffer->IpAddrsList[i]);
}
}
pIpAddrBuffer->IpAddrsList[i] = 0;
//
// Check the IP addr list.
//
Status = CheckIPAddresses(pIpAddrBuffer, &GoodAddr, FALSE);
if (Status == NO_ERROR)
{
pIpAddrBuffer->Resolved = TRUE;
pIpAddrBuffer->IpAddrsList[0] = htonl(GoodAddr);
pIpAddrBuffer->IpAddrsList[1] = 0;
if (Trace)
{
DbgPrint("LmhSvc: SUCCESS -- Dns address = <%x>\n", pIpAddrBuffer->IpAddrsList[0]);
}
}
else
{
pIpAddrBuffer->IpAddrsList[0] = 0;
if (Trace)
{
DbgPrint("LmhSvc: CheckIPAddresses returned <%x>\n", Status);
}
}
}
//------------------------------------------------------------------------
LONG
GetHostName(
IN HANDLE fd,
IN tIPADDR_BUFFER_DNS *pIpAddrBuffer
)
/*++
Routine Description:
This procedure attempts to resolve a name using the Resolver through
the Sockets interface to DNS.
Arguments:
Return Value:
0 if successful, -1 otherwise.
--*/
{
LONG status;
ULONG NameLen;
ULONG IpAddr;
PWSAQUERYSETW pwsaq = (PWSAQUERYSETW) pWSABuffer;
INT err;
HANDLE hRnR;
PWSTR szHostnameW;
BYTE *pAllocatedBuffer = NULL;
DWORD dwLength;
pIpAddrBuffer->Resolved = FALSE;
// Hostname is encoded with OEMCP, so we convert from OEMCP->Unicode
if (pIpAddrBuffer->bUnicode) {
NameLen = pIpAddrBuffer->NameLen;
ASSERT((NameLen % sizeof(WCHAR)) == 0);
NameLen /= sizeof(WCHAR);
} else {
WCHAR uncName[DNS_NAME_BUFFER_LENGTH];
ASSERT(pIpAddrBuffer->NameLen < DNS_NAME_BUFFER_LENGTH);
pIpAddrBuffer->pName[pIpAddrBuffer->NameLen] = 0;
MultiByteToWideChar (CP_OEMCP, 0, pIpAddrBuffer->pName, -1, uncName, sizeof(uncName)/sizeof(WCHAR));
NameLen = wcslen(uncName);
memcpy (pIpAddrBuffer->pwName, uncName, NameLen * sizeof(WCHAR));
pIpAddrBuffer->pwName[NameLen] = 0;
}
szHostnameW = pIpAddrBuffer->pwName;
// truncate spaces from the end for netbios names
//
if (NameLen < NETBIOS_NAMESIZE)
{
//
// Start from the end and find the first non-space character
//
NameLen = NETBIOS_NAMESIZE-1;
while ((NameLen) && (szHostnameW[NameLen-1] == 0x20))
{
NameLen--;
}
szHostnameW[NameLen] = '\0';
}
if (!NameLen || !SocketsUp) {
if (Trace) {
DbgPrint("Lmhsvc: Failed to Resolve name, NameLen=<%d>\n", NameLen);
}
goto label_exit;
}
//
// do a lookup using RNR
//
if (Trace) {
DbgPrint("Lmhsvc: Resolving name = \"%ws\", NameLen=<%d>\n", szHostnameW, NameLen);
}
RtlZeroMemory(pwsaq, sizeof(*pwsaq));
pwsaq->dwSize = sizeof(*pwsaq);
pwsaq->lpszServiceInstanceName = szHostnameW;
pwsaq->lpServiceClassId = &HostnameGuid;
pwsaq->dwNameSpace = NS_DNS;
err = WSALookupServiceBeginW (pwsaq, LUP_RETURN_NAME| LUP_RETURN_ADDR| LUP_RETURN_ALIASES, &hRnR);
if(err != NO_ERROR) {
if (Trace) {
DbgPrint("LmhSvc: WSALookupServiceBeginA returned <%x>, Error=<%d>\n", err, GetLastError());
}
goto label_exit;
}
//
// The query was accepted, so execute it via the Next call.
//
dwLength = WSA_QUERY_BUFFER_LENGTH;
err = WSALookupServiceNextW (hRnR, 0, &dwLength, pwsaq);
if (err != NO_ERROR)
{
err = GetLastError();
} else if (pwsaq->dwNumberOfCsAddrs) {
GetHostNameCopyBack(pIpAddrBuffer, pwsaq);
/* check if there is any alias available */
err = WSALookupServiceNextW (hRnR, 0, &dwLength, pwsaq);
if (err != NO_ERROR) {
err = GetLastError();
if (err != WSAEFAULT) {
err = NO_ERROR; // Ignore this error
}
} else if (pwsaq->dwOutputFlags & RESULT_IS_ALIAS) {
GetHostNameCopyBack(pIpAddrBuffer, pwsaq);
}
}
WSALookupServiceEnd (hRnR);
if ((WSAEFAULT == err) &&
(pAllocatedBuffer = malloc(2*dwLength)))
{
if (Trace)
{
DbgPrint("\tLmhsvc: WSALookupServiceNextW ==> WSAEFAULT: Retrying, BufferLength=<%d>-><2*%d> ...\n",
WSA_QUERY_BUFFER_LENGTH, dwLength);
}
dwLength *= 2;
pwsaq = (PWSAQUERYSETW) pAllocatedBuffer;
RtlZeroMemory(pwsaq, sizeof(*pwsaq));
pwsaq->dwSize = sizeof(*pwsaq);
pwsaq->lpszServiceInstanceName = szHostnameW;
pwsaq->lpServiceClassId = &HostnameGuid;
pwsaq->dwNameSpace = NS_DNS;
err = WSALookupServiceBeginW(pwsaq, LUP_RETURN_NAME| LUP_RETURN_ADDR| LUP_RETURN_ALIASES, &hRnR);
if(err == NO_ERROR)
{
err = WSALookupServiceNextW (hRnR, 0, &dwLength, pwsaq);
if (err == NO_ERROR && pwsaq->dwNumberOfCsAddrs) {
GetHostNameCopyBack(pIpAddrBuffer, pwsaq);
if (WSALookupServiceNextW (hRnR, 0, &dwLength, pwsaq) == NO_ERROR) {
if (pwsaq->dwOutputFlags & RESULT_IS_ALIAS) {
GetHostNameCopyBack(pIpAddrBuffer, pwsaq);
}
}
}
WSALookupServiceEnd (hRnR);
}
}
if (err != NO_ERROR)
{
if (Trace)
{
DbgPrint("LmhSvc: WSALookupServiceNextW returned <%x>, NumAddrs=<%d>, Error=<%d>, dwLength=<%d>\n",
err, pwsaq->dwNumberOfCsAddrs, GetLastError(), dwLength);
}
}
label_exit:
if (pAllocatedBuffer) {
free(pAllocatedBuffer);
}
status = PostForGetHostByName(fd);
return(status);
}
#include <ipexport.h>
#include <icmpapi.h>
#define DEFAULT_BUFFER_SIZE (0x2000 - 8)
#define DEFAULT_SEND_SIZE 32
#define DEFAULT_COUNT 2
#define DEFAULT_TTL 32
#define DEFAULT_TOS 0
#define DEFAULT_TIMEOUT 2000L // default timeout set to 2 secs.
LONG
CheckIPAddresses(
IN tIPADDR_BUFFER_DNS *pIpAddrBuffer,
IN ULONG *IpAddr,
IN BOOLEAN fOrdered
)
/*++
Routine Description:
This function checks a list of IP addrs for reachability by pinging each in turn
until a successful one is found. This function assumes that the list of addresses
is terminated by a 0 address.
Arguments:
Return Value:
0 if successful, -1 otherwise.
--*/
{
ULONG i;
ULONG *pIpAddrs;
HANDLE IcmpHandle;
PUCHAR pSendBuffer;
PUCHAR pRcvBuffer;
ULONG address = 0;
ULONG result;
ULONG status;
ULONG numberOfReplies;
IP_OPTION_INFORMATION SendOpts;
if (!(pSendBuffer = malloc(DEFAULT_SEND_SIZE)) ||
(!(pRcvBuffer = malloc(DEFAULT_BUFFER_SIZE))))
{
if (Trace)
{
DbgPrint("LmhSvc.CheckIPAddresses: ERROR -- malloc failed for %s\n",
(pSendBuffer ? "pRcvBuffer" : "pSendBuffer"));
}
if (pSendBuffer)
{
free (pSendBuffer);
}
return -1;
}
//
// Open channel
//
IcmpHandle = IcmpCreateFile();
if (IcmpHandle == INVALID_HANDLE_VALUE)
{
DBG_PRINT ( "Unable to contact IP driver, error code %d.\n", GetLastError() );
free (pSendBuffer);
free (pRcvBuffer);
return -1;
}
//
// init to the first address.
//
pIpAddrs = pIpAddrBuffer->IpAddrsList;
*IpAddr = (fOrdered) ? *pIpAddrs : htonl(*pIpAddrs);
//
// Initialize the send buffer pattern.
//
for (i = 0; i < DEFAULT_SEND_SIZE; i++)
{
pSendBuffer[i] = (UCHAR)('A' + (i % 23));
}
//
// Initialize the send options
//
SendOpts.OptionsData = NULL;
SendOpts.OptionsSize = 0;
SendOpts.Ttl = DEFAULT_TTL;
SendOpts.Tos = DEFAULT_TOS;
SendOpts.Flags = 0;
//
// For each IP address in the list
//
while (*pIpAddrs)
{
struct in_addr addr;
address = (fOrdered) ? *pIpAddrs : htonl(*pIpAddrs);
addr.s_addr = address;
if (address == INADDR_BROADCAST)
{
if (Trace)
{
DbgPrint("LmhSvc: Cannot ping a Broadcast address = <%s>\n", inet_ntoa(addr));
}
pIpAddrs++;
continue;
}
for (i=0; i < DEFAULT_COUNT; i++)
{
if (Trace)
{
DbgPrint("LmhSvc: Pinging <%s>\n", inet_ntoa(addr));
}
numberOfReplies = IcmpSendEcho (IcmpHandle,
address,
pSendBuffer,
(unsigned short) DEFAULT_SEND_SIZE,
&SendOpts,
pRcvBuffer,
DEFAULT_BUFFER_SIZE, // pRcvBuffer size!
DEFAULT_TIMEOUT);
//
// If ping successful, return the IP address
//
if (numberOfReplies != 0)
{
PICMP_ECHO_REPLY reply;
reply = (PICMP_ECHO_REPLY) pRcvBuffer;
if (reply->Status == IP_SUCCESS)
{
if (Trace)
{
DbgPrint("LmhSvc: SUCCESS: Received <%d> replies after Pinging <%s>\n",
numberOfReplies, inet_ntoa(addr));
}
result = IcmpCloseHandle(IcmpHandle);
*IpAddr = address;
free (pSendBuffer);
free (pRcvBuffer);
return 0;
}
}
}
if (Trace)
{
DbgPrint("LmhSvc: FAILed: Pinging <%s>\n", inet_ntoa(addr));
}
pIpAddrs++;
}
result = IcmpCloseHandle(IcmpHandle);
//
// Return the first addr if none matched in the hope that TCP session setup might succeed even though
// the pings failed.
//
free (pSendBuffer);
free (pRcvBuffer);
return NO_ERROR;
}
ULONG
VerifyIPAddresses(
IN HANDLE fd,
IN tIPADDR_BUFFER_DNS *pIpAddrBuffer
)
/*++
Routine Description:
This function finds out the reachable IP addr and returns the Irp to Netbt
Arguments:
Return Value:
NONE
--*/
{
DWORD Status;
ULONG GoodAddr;
pIpAddrBuffer->Resolved = FALSE;
Status = CheckIPAddresses(pIpAddrBuffer, &GoodAddr, TRUE);
if (Status == NO_ERROR) {
pIpAddrBuffer->IpAddrsList[0] = ntohl(GoodAddr);
//
// NULL terminate
//
pIpAddrBuffer->IpAddrsList[1] = 0;
pIpAddrBuffer->Resolved = TRUE;
} else {
pIpAddrBuffer->IpAddrsList[0] = 0;
}
Status = PostForCheckIPAddr(fd);
return Status;
}
VOID
CheckIPAddrWorkerRtn(
IN LPVOID lpParam
)
/*++
Routine Description:
This function submits IP address check Irps into Netbt, on completion of the Irp, it submits the IP address
list to CheckIPAddresses.
Arguments:
Return Value:
NONE
--*/
{
HANDLE EventList[2];
DWORD status;
HANDLE hNbt;
ULONG EventCount;
LONG err;
LONG Value;
LARGE_INTEGER Timeout = RtlEnlargedIntegerMultiply (-10 * 60, 1000 * 1000 * 10); // 10 minutes
assert(**lpParam == 1);
//
// ignore the return code from resyncNbt().
//
// In most cases (no domains spanning an ip router), it is not a
// catastrophe if nbt.sys couldn't successfully process the NBT_RESYNC
// ioctl command. Since I'm ignoring the return, I announce I'm running
// before I call it to allow other dependent services to start.
//
//
status = PrimeCacheNbt(&hNbt, 1);
if (Trace)
{
DbgPrint("LMHSVC: Entered CheckIPAddrWorkerRtn, hNbt %lx.\n", hNbt);
}
if (hNbt != (HANDLE)-1)
{
status = PostForCheckIPAddr(hNbt);
if (status == NO_ERROR)
{
EventCount = 2;
}
else
{
if (Trace)
{
DbgPrint("Lmhsvc:Error posting Irp for get host by name\n");
}
EventCount = 1;
}
}
else
{
EventCount = 1;
}
//
// "A SERVICE_MAIN_FUNCTION does not return until the service is ready
// to terminate."
//
// (ref: api32wh.hlp, SERVICE_MAIN_FUNCTION)
//
//
assert(Poison[1]);
EventList[0] = Poison[1];
EventList[1] = NbtEvent[1];
while (TRUE)
{
status = NtWaitForMultipleObjects(
EventCount,
EventList,
WaitAny, // wait for any event
FALSE,
(EventCount == 1)? &Timeout: NULL);
if (status == WAIT_TIMEOUT)
{
if (hNbt == (HANDLE)-1)
{
PrimeCacheNbt(&hNbt, 1);
if (hNbt == (HANDLE)-1)
{
continue; // to wait
}
}
status = PostForCheckIPAddr(hNbt); // try again
if (status == NO_ERROR)
{
EventCount = 2;
}
}
else if (status == 1)
{
if (Trace)
{
DbgPrint("LMHSVC: Doing VerifyAddr\n");
}
// the irp used for gethostby name has returned
status = VerifyIPAddresses(hNbt, pIpAddrBufferChkIP);
//
// disable the get host by name stuff if we have an error
// posting a buffer to the transport
//
if (status != NO_ERROR)
{
EventCount = 1;
}
}
else
{
// it must have been a the Poison event signalling the end of the
// the service, so exit after getting the Irp back from the
// transport. This system will look after canceling the IO and
// getting the Irp back.
NtClose(Poison[1]);
NtClose(NbtEvent[1]);
NtClose(hNbt);
break;
}
}
if (Trace)
{
DBG_PRINT ("LMHSVC: Exiting [CheckIPAddrWorkerRtn] now!\n");
}
ExitThread(NO_ERROR);
return;
}