|
|
/*++
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; }
|