mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
602 lines
14 KiB
602 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
gethost.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the a version of GetHostByName for IPX/SPX for
|
|
dos and windows.
|
|
|
|
Author:
|
|
|
|
31 May 94 AlexMit
|
|
|
|
15 Oct 94 JRoberts - changed to avoid using Novell headers and libs
|
|
--*/
|
|
|
|
|
|
#include "sysinc.h"
|
|
#include "rpc.h"
|
|
#include "rpctran.h"
|
|
#include "novell.h"
|
|
#include "gethost.h"
|
|
#include "netcons.h"
|
|
#include "ncb.h"
|
|
|
|
#ifdef WIN
|
|
# include "windows.h"
|
|
# define I_RpcAllocate (*(RpcRuntimeInfo->Allocate))
|
|
# define I_RpcFree (*(RpcRuntimeInfo->Free))
|
|
#else
|
|
# include "regalloc.h"
|
|
#endif
|
|
|
|
#define NAME_LEN 48
|
|
#define SAP_ECB_COUNT 5
|
|
|
|
/********************************************************************/
|
|
|
|
#ifdef WIN
|
|
#define CACHE_LENGTH 16
|
|
#else
|
|
#define CACHE_LENGTH 4
|
|
#endif
|
|
|
|
#define CACHE_EXPIRATION_TIME (10 * 60 * 18)
|
|
|
|
struct
|
|
{
|
|
char Name[16];
|
|
IPX_ADDRESS Address;
|
|
unsigned long Time;
|
|
}
|
|
ServerCache[CACHE_LENGTH];
|
|
|
|
/********************************************************************/
|
|
|
|
extern int atoi(const char *);
|
|
|
|
/********************************************************************/
|
|
unsigned char chtob( unsigned char c1, unsigned char c2 )
|
|
/* Convert two hex digits (stored as ascii) into one byte. */
|
|
|
|
{
|
|
unsigned char out;
|
|
|
|
if (c1 >= '0' && c1 <= '9')
|
|
out = (c1 - '0') << 4;
|
|
else
|
|
{
|
|
if (c1 >= 'a' && c1 <= 'f')
|
|
out = (c1 - 'a' + 10) << 4;
|
|
else if (c1 >= 'A' && c1 <= 'F')
|
|
out = (c1 - 'A' + 10) << 4;
|
|
else
|
|
out = 0;
|
|
}
|
|
|
|
if (c2 >= '0' && c2 <= '9')
|
|
out |= c2 -'0';
|
|
else
|
|
{
|
|
if (c2 >= 'a' && c2 <= 'f')
|
|
out |= c2 - 'a' + 10;
|
|
else if (c2 >= 'A' && c2 <= 'F')
|
|
out |= c2 - 'A' + 10;
|
|
else
|
|
out = 0;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
unsigned char chtob( unsigned char c1, unsigned char c2 );
|
|
RPC_STATUS FromBindery( IPX_ADDRESS __RPC_FAR *host,
|
|
RPC_CHAR __RPC_FAR *name );
|
|
|
|
unsigned long
|
|
DosGetTickCount(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the number of ticks since the last time it was midnight.
|
|
There are 65536 ticks per hour.
|
|
|
|
--*/
|
|
|
|
{
|
|
_asm
|
|
{
|
|
push bp
|
|
push si
|
|
push di
|
|
|
|
xor ax, ax
|
|
int 0x1a
|
|
mov ax, dx
|
|
mov dx, cx
|
|
|
|
pop di
|
|
pop si
|
|
pop bp
|
|
}
|
|
}
|
|
|
|
void
|
|
AddServerToCache(
|
|
char PAPI * Name,
|
|
IPX_ADDRESS PAPI * Address
|
|
)
|
|
{
|
|
unsigned i;
|
|
|
|
//
|
|
// If the server is already in the table, overwrite the existing entry.
|
|
//
|
|
for (i=0; i < CACHE_LENGTH; ++i)
|
|
{
|
|
if (0 == _fstrnicmp(ServerCache[i].Name, Name, 16))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If it is not in the table, try to find an empty entry to fill.
|
|
//
|
|
if (i == CACHE_LENGTH)
|
|
{
|
|
for (i=0; i < CACHE_LENGTH; ++i)
|
|
{
|
|
if (ServerCache[i].Name[0] == '\0')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If all entries are full, overwrite the oldest one.
|
|
//
|
|
if (i == CACHE_LENGTH)
|
|
{
|
|
unsigned long BestTime = ~0;
|
|
unsigned BestIndex;
|
|
|
|
for (i=0; i < CACHE_LENGTH; ++i)
|
|
{
|
|
if (ServerCache[i].Time <= BestTime)
|
|
{
|
|
BestTime = ServerCache[i].Time;
|
|
BestIndex = i;
|
|
}
|
|
}
|
|
|
|
i = BestIndex;
|
|
}
|
|
|
|
//
|
|
// Update the entry's information.
|
|
//
|
|
_fstrcpy(ServerCache[i].Name, Name);
|
|
_fmemcpy(&ServerCache[i].Address, Address, sizeof(IPX_ADDRESS));
|
|
|
|
ServerCache[i].Time = DosGetTickCount();
|
|
}
|
|
|
|
IPX_ADDRESS *
|
|
FindServerInCache(
|
|
char PAPI * Name
|
|
)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i=0; i < CACHE_LENGTH; ++i)
|
|
{
|
|
if (0 == _fstrcmp(ServerCache[i].Name, Name))
|
|
{
|
|
return &ServerCache[i].Address;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
CachedServerContacted(
|
|
char PAPI * Name
|
|
)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i=0; i < CACHE_LENGTH; ++i)
|
|
{
|
|
if (0 == _fstrcmp(ServerCache[i].Name, Name))
|
|
{
|
|
ServerCache[i].Time = DosGetTickCount();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CachedServerNotContacted(
|
|
char PAPI * Name
|
|
)
|
|
{
|
|
BOOL Flushed = FALSE;
|
|
unsigned i;
|
|
|
|
for (i=0; i < CACHE_LENGTH; ++i)
|
|
{
|
|
if (0 == _fstrcmp(ServerCache[i].Name, Name))
|
|
{
|
|
if (DosGetTickCount() - ServerCache[i].Time > CACHE_EXPIRATION_TIME)
|
|
{
|
|
ServerCache[i].Name[0] = '\0';
|
|
Flushed = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Flushed;
|
|
}
|
|
|
|
BOOL PreferredServerFound = FALSE;
|
|
NETWARE_CONNECTION PreferredServer;
|
|
|
|
|
|
RPC_STATUS
|
|
SearchBindery(
|
|
RPC_CHAR __RPC_FAR *name,
|
|
IPX_ADDRESS __RPC_FAR *Address,
|
|
unsigned BindingTimeout
|
|
)
|
|
{
|
|
char buffer[128];
|
|
unsigned SapTickCount;
|
|
unsigned RetryCount = 0;
|
|
|
|
//
|
|
//
|
|
//
|
|
if (FALSE == PreferredServerFound)
|
|
{
|
|
reconnect:
|
|
|
|
PreferredServer.TickLimit = 19;
|
|
SapTickCount = 19;
|
|
|
|
if (BindingTimeout > RPC_C_BINDING_DEFAULT_TIMEOUT)
|
|
{
|
|
PreferredServer.TickLimit <<= BindingTimeout - RPC_C_BINDING_DEFAULT_TIMEOUT;
|
|
SapTickCount <<= BindingTimeout - RPC_C_BINDING_DEFAULT_TIMEOUT;
|
|
}
|
|
|
|
ConnectToAnyFileServer(&PreferredServer, SapTickCount);
|
|
|
|
if (PreferredServer.ReturnCode || PreferredServer.ConnectionStatus)
|
|
{
|
|
DisconnectFromServer(&PreferredServer);
|
|
PreferredServerFound = FALSE;
|
|
return RPC_S_SERVER_UNAVAILABLE;
|
|
}
|
|
|
|
PreferredServerFound = TRUE;
|
|
}
|
|
|
|
//
|
|
// Read from the bindery.
|
|
//
|
|
ReadPropertyValue(&PreferredServer,
|
|
name,
|
|
RPC_SAP_TYPE,
|
|
"NET_ADDRESS",
|
|
1,
|
|
buffer,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (PreferredServer.ReturnCode)
|
|
{
|
|
DisconnectFromServer(&PreferredServer);
|
|
PreferredServerFound = FALSE;
|
|
|
|
if (RetryCount++)
|
|
{
|
|
return RPC_S_SERVER_UNAVAILABLE;
|
|
}
|
|
else
|
|
{
|
|
goto reconnect;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the host address.
|
|
//
|
|
_fmemcpy( Address, buffer, 10 );
|
|
|
|
DisconnectFromServer(&PreferredServer);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
/********************************************************************/
|
|
RPC_STATUS
|
|
SearchWithSap(
|
|
RPC_CHAR __RPC_FAR *name,
|
|
IPX_ADDRESS __RPC_FAR *host,
|
|
unsigned Timeout
|
|
|
|
#ifdef WIN
|
|
, RPC_CLIENT_RUNTIME_INFO PAPI * RpcRuntimeInfo
|
|
#endif
|
|
)
|
|
{
|
|
WORD start;
|
|
unsigned SapSocket;
|
|
int result;
|
|
int i;
|
|
int retry;
|
|
|
|
struct
|
|
{
|
|
ECB ecb;
|
|
IPX_HEADER ipx;
|
|
SAP_REQUEST req;
|
|
} send;
|
|
|
|
struct SAP_RESPONSE_ECB
|
|
{
|
|
ECB ecb;
|
|
IPX_HEADER ipx;
|
|
SAP_RESPONSE resp;
|
|
}
|
|
__RPC_FAR * mem;
|
|
|
|
RPC_STATUS status = RPC_S_SERVER_UNAVAILABLE;
|
|
|
|
//
|
|
// The base timeout is two seconds; max is a minute.
|
|
//
|
|
if (Timeout <= RPC_C_BINDING_DEFAULT_TIMEOUT)
|
|
{
|
|
Timeout = 18 * 2;
|
|
}
|
|
else
|
|
{
|
|
Timeout = (18 * 2) << (Timeout - RPC_C_BINDING_DEFAULT_TIMEOUT);
|
|
}
|
|
|
|
result = IPXOpenSocket(TASKID_C &SapSocket, 0);
|
|
if (result != 0)
|
|
return RPC_S_OUT_OF_RESOURCES;
|
|
|
|
// Allocate memory for ECBs.
|
|
#ifdef WIN
|
|
mem = I_RpcAllocate( SAP_ECB_COUNT * sizeof(*mem) );
|
|
#else
|
|
mem = I_RpcRegisteredBufferAllocate( SAP_ECB_COUNT * sizeof(*mem) );
|
|
#endif
|
|
|
|
if (mem == NULL)
|
|
{
|
|
status = RPC_S_OUT_OF_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
_fmemset( mem, 0, SAP_ECB_COUNT * sizeof(mem) );
|
|
|
|
//
|
|
// Post some ECBs.
|
|
//
|
|
for (i = 0; i < SAP_ECB_COUNT; i++)
|
|
{
|
|
SetupEcb(&mem[i].ecb, SapSocket, sizeof(SAP_RESPONSE));
|
|
IPXListenForPacket(TASKID_C &mem[i].ecb );
|
|
}
|
|
|
|
//
|
|
// We want to send a SAP request and scan responses for the server
|
|
// that we want. We transmit the request up to two times because
|
|
// servers sometimes miss a single broadcast.
|
|
//
|
|
for (retry = 0; retry < 2 ; ++retry)
|
|
{
|
|
send.ipx.PacketType = IPX_PACKET_TYPE;
|
|
send.ipx.Destination.Network = 0;
|
|
send.ipx.Destination.Socket = SAP_SOCKET;
|
|
_fmemset( send.ipx.Destination.Node, 0xff, sizeof(send.ipx.Destination.Node) );
|
|
|
|
SetupEcb(&send.ecb, SapSocket, sizeof(send));
|
|
_fmemset( send.ecb.immediateAddress, 0xff, sizeof(send.ecb.immediateAddress) );
|
|
|
|
// Send the data.
|
|
|
|
send.req.QueryType = SAP_GENERAL_QUERY;
|
|
send.req.ServerType = RPC_SAP_TYPE_SWAPPED;
|
|
|
|
IPXSendPacket(TASKID_C &send.ecb );
|
|
|
|
while (send.ecb.inUseFlag)
|
|
IPXRelinquishControl();
|
|
|
|
// Verify that the send was successful.
|
|
|
|
if (send.ecb.completionCode)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Get packets till timeout is returned or a good reply is returned.
|
|
//
|
|
start = IPXGetIntervalMarker(TASKID);
|
|
do
|
|
{
|
|
struct SAP_RESPONSE_ECB __RPC_FAR * curr;
|
|
|
|
for (curr = mem; curr < mem + SAP_ECB_COUNT; curr++)
|
|
{
|
|
if (curr->ecb.inUseFlag == 0)
|
|
{
|
|
if (curr->ecb.completionCode == 0x00)
|
|
{
|
|
// Verify the packet.
|
|
//
|
|
curr->ipx.Length = ByteSwapShort( curr->ipx.Length );
|
|
curr->ipx.Length -= sizeof(IPX_HEADER);
|
|
|
|
if (curr->ipx.Source.Socket == SAP_SOCKET &&
|
|
curr->ipx.Length >= sizeof (SAP_RESPONSE) &&
|
|
curr->resp.PacketType == SAP_GENERAL_RESPONSE )
|
|
{
|
|
unsigned num_entry = curr->ipx.Length / sizeof(SAP_ENTRY);
|
|
for (i = 0; i < num_entry; i++)
|
|
{
|
|
if (0 == _fstrnicmp( name, curr->resp.Entries[i].Name, NAME_LEN))
|
|
{
|
|
// Only copy the network and node numbers, not the socket.
|
|
//
|
|
_fmemcpy( host, &curr->resp.Entries[i].Address, 10 );
|
|
status = RPC_S_OK;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Repost the receive.
|
|
//
|
|
IPXListenForPacket( TASKID_C &curr->ecb );
|
|
}
|
|
}
|
|
IPXRelinquishControl();
|
|
}
|
|
while (IPXGetIntervalMarker(TASKID) - start < Timeout);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
//
|
|
// Cancel the ECBs.
|
|
//
|
|
if (mem)
|
|
{
|
|
for (i = 0; i < SAP_ECB_COUNT; i++)
|
|
{
|
|
if (mem[i].ecb.inUseFlag)
|
|
{
|
|
IPXCancelEvent( TASKID_C &mem[i].ecb );
|
|
while (mem[i].ecb.inUseFlag)
|
|
{
|
|
IPXRelinquishControl();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the memory.
|
|
//
|
|
#ifdef WIN
|
|
I_RpcFree( mem );
|
|
#else
|
|
I_RpcRegisteredBufferFree( mem );
|
|
#endif
|
|
}
|
|
|
|
IPXCloseSocket(TASKID_C SapSocket);
|
|
|
|
return status;
|
|
}
|
|
|
|
/********************************************************************/
|
|
RPC_STATUS
|
|
IpxGetHostByName(
|
|
RPC_CHAR __RPC_FAR * name,
|
|
IPX_ADDRESS __RPC_FAR * Address,
|
|
RPC_CHAR __RPC_FAR * endpoint,
|
|
unsigned Timeout
|
|
#ifdef WIN
|
|
, RPC_CLIENT_RUNTIME_INFO * RpcClientRuntimeInfo
|
|
#endif
|
|
)
|
|
{
|
|
RPC_STATUS status;
|
|
int i;
|
|
int length;
|
|
IPX_ADDRESS * CachedAddress;
|
|
|
|
// Set the endpoint.
|
|
Address->Socket = ByteSwapShort(atoi(endpoint));
|
|
|
|
// Fail if no address was specified.
|
|
if (name == NULL || name[0] == '\0')
|
|
return RPC_S_SERVER_UNAVAILABLE;
|
|
|
|
// If the name starts with ~, convert it directly to a network address.
|
|
length = _fstrlen(name);
|
|
if (name[0] == '~')
|
|
{
|
|
unsigned char __RPC_FAR * Temp;
|
|
|
|
if (length != 21)
|
|
return RPC_S_INVALID_NET_ADDR;
|
|
|
|
Temp = (unsigned char __RPC_FAR *) &Address->Network;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
Temp[i] = chtob( name[2*i + 1], name[2*i + 2] );
|
|
}
|
|
|
|
Temp = (unsigned char __RPC_FAR *) Address->Node;
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
Temp[i] = chtob( name[2*i + 9], name[2*i + 10] );
|
|
}
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
if (length > 15)
|
|
{
|
|
return RPC_S_INVALID_NET_ADDR;
|
|
}
|
|
|
|
CachedAddress = FindServerInCache(name);
|
|
if (CachedAddress)
|
|
{
|
|
_fmemcpy(Address, CachedAddress, 10);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
// Try the bindery.
|
|
status = SearchBindery(name, Address, Timeout);
|
|
if (status && !PreferredServerFound)
|
|
{
|
|
status = SearchWithSap(name,
|
|
Address,
|
|
Timeout
|
|
#ifdef WIN
|
|
, RpcClientRuntimeInfo
|
|
#endif
|
|
);
|
|
}
|
|
|
|
if (!status)
|
|
{
|
|
AddServerToCache(name, Address);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|