Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2095 lines
50 KiB

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
Nsptcpip.c
Abstract:
This module contains support for the Name Space Provider for TCPIP,
which includes four actual NSPs:
- local lookup for "loopback", "localhost", and the host name.
- hosts file lookup.
- DNS queries
- WINS (Netbt) lookups
Author:
David Treadwell (davidtr) 22-Apr-1994
Revision History:
--*/
#if defined(CHICAGO)
#undef UNICODE
#else
#define UNICODE
#define _UNICODE
#endif
#include "winsockp.h"
#include <nspapi.h>
#include <nspapip.h>
#include <svcguid.h>
#include <nspmisc.h>
#define host_aliases ACCESS_THREAD_DATA( host_aliases, GETHOST )
extern GUID HostnameGuid;
#define DNS_PORT (htons(53))
#define HOSTENT_CACHING_ENABLED
#if defined(CHICAGO)
struct hostent * localhostent ( void);
struct hostent * myhostent ( void);
#define UDP_NAME "UDP/IP"
#define TCP_NAME "TCP/IP"
#if PACKETSZ > 1024
#define MAXPACKET PACKETSZ
#else
#define MAXPACKET 1024
#endif
typedef union {
HEADER hdr;
unsigned char buf[MAXPACKET];
} querybuf;
struct hostent *
getanswer(
IN LPSOCK_THREAD pThread,
OUT querybuf *answer,
OUT int * ttl,
IN int anslen,
IN int iquery
);
LPHOSTENT
GetHostentFromName(
IN LPSOCK_THREAD pThread,
IN LPSTR Name
);
#define _gethtbyname(name) GetHostentFromName(pThread, (LPSTR)name)
ULONG
SockNbtResolveName (
IN PCHAR Name
);
struct hostent *
dnshostent (
void
);
DWORD
CopyHostentToBuffer (
char FAR *Buffer,
int BufferLength,
PHOSTENT Hostent
);
DWORD
BytesInHostent (
PHOSTENT Hostent
);
#endif // defined(CHICAGO)
#if defined(HOSTENT_CACHING_ENABLED)
LIST_ENTRY HostentCacheListHead = { &HostentCacheListHead, &HostentCacheListHead };
DWORD MaxHostentCacheSize = 10;
DWORD CurrentHostentCacheSize = 0;
#if defined(CHICAGO)
#define MY_LARGE_INTEGER DWORD
#define QUADPART(x) (x)
#define LSTRCMPI lstrcmpi
#define REG_OPEN_KEY_EX RegOpenKeyExA
#else
#define MY_LARGE_INTEGER LARGE_INTEGER
#define QUADPART(x) (x).QuadPart
#define LSTRCMPI _stricmp
#define REG_OPEN_KEY_EX RegOpenKeyExW
#endif
typedef struct _HOSTENT_CACHE_ENTRY {
LIST_ENTRY HostentListEntry;
MY_LARGE_INTEGER LastAccessTime;
MY_LARGE_INTEGER ExpirationTime;
HOSTENT HostEntry;
// BYTE[*]; // other hostent info
} HOSTENT_CACHE_ENTRY, *PHOSTENT_CACHE_ENTRY;
VOID
CacheHostent (
IN PHOSTENT HostEntry,
IN INT Ttl
);
PHOSTENT
QueryHostentCache (
IN LPSTR Name OPTIONAL,
IN DWORD IpAddress OPTIONAL
);
#endif // defined(HOSTENT_CACHING_ENABLED)
#define NSP_VERSION 1
VOID
CopyToAliasBuffer (
IN PSTR Alias,
IN OUT LPTSTR AliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN OUT LPDWORD AliasBytesUsed
);
INT
GuidToPort (
IN LPGUID lpServiceType,
IN LPINT lpiProtocols,
OUT LPWORD Port,
OUT LPBOOL IsTcp
);
INT
HostentToCsaddr (
IN PHOSTENT HostEntry,
IN WORD Port,
IN BOOL IsTcp,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPTSTR lpAliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN OUT LPDWORD Count
);
VOID
ReadPriorities (
VOID
);
INT
ServiceCsaddr (
IN WORD Port,
IN BOOL IsTcp,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength
);
INT
APIENTRY
TcpipLocalGetAddressByName (
IN LPGUID lpServiceType,
IN LPTSTR lpServiceName,
IN LPINT lpiProtocols,
IN DWORD dwResolution,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPTSTR lpAliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN HANDLE hCancellationEvent
);
DWORD
APIENTRY
TcpipLocalSetService (
IN DWORD dwOperation,
IN DWORD dwFlags,
IN BOOL fUnicodeBlob,
IN LPSERVICE_INFO lpServiceInfo
);
INT
APIENTRY
TcpipHostsGetAddressByName (
IN LPGUID lpServiceType,
IN LPTSTR lpServiceName,
IN LPINT lpiProtocols,
IN DWORD dwResolution,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPTSTR lpAliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN HANDLE hCancellationEvent
);
INT
APIENTRY
TcpipDnsGetAddressByName (
IN LPGUID lpServiceType,
IN LPTSTR lpServiceName,
IN LPINT lpiProtocols,
IN DWORD dwResolution,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPTSTR lpAliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN HANDLE hCancellationEvent
);
INT
APIENTRY
TcpipNetbtGetAddressByName (
IN LPGUID lpServiceType,
IN LPTSTR lpServiceName,
IN LPINT lpiProtocols,
IN DWORD dwResolution,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPTSTR lpAliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN HANDLE hCancellationEvent
);
DWORD LocalPriority = NS_STANDARD_FAST_PRIORITY - 1;
DWORD HostsPriority = NS_STANDARD_FAST_PRIORITY;
DWORD DnsPriority = NS_STANDARD_PRIORITY;
DWORD NetbtPriority = NS_STANDARD_PRIORITY + 1;
INT
APIENTRY
NPLoadNameSpaces (
IN OUT LPDWORD lpdwVersion,
IN OUT LPNS_ROUTINE nsrBuffer,
IN OUT LPDWORD lpdwBufferLength
)
{
DWORD bufferLengthRequired;
PNS_ROUTINE nsRoutineLocal;
PNS_ROUTINE nsRoutineHosts;
PNS_ROUTINE nsRoutineDns;
PNS_ROUTINE nsRoutineNetbt;
//
// Calculate the size of a buffer the caller will need to specify.
// We support four name spaces in this DLL, each of which exports
// a single NSP API.
//
bufferLengthRequired = 4 * (sizeof(NS_ROUTINE) + (4 * sizeof(LPFN_NSPAPI)) );
//
// If the caller specified too small a buffer, fail and tell them
// how large a buffer is required.
//
if ( bufferLengthRequired > *lpdwBufferLength ) {
*lpdwBufferLength = bufferLengthRequired;
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return -1;
}
//
// Read the name space priorities from the registry.
//
ReadPriorities( );
//
// Tell the caller the version of the NSP API interface which we
// support.
//
*lpdwVersion = 1;
//
// Calculate where in the buffer we'll put the NS_ROUTINE structures
// for each of our name spaces.
//
nsRoutineLocal = nsrBuffer;
nsRoutineHosts = nsrBuffer + 1;
nsRoutineDns = nsrBuffer + 2;
nsRoutineNetbt = nsrBuffer + 3;
//
// Calculate where in the buffer each of the function pointer arrays
// will go.
//
nsRoutineLocal->alpfnFunctions = (LPFN_NSPAPI *)( nsRoutineNetbt + 1 );
nsRoutineHosts->alpfnFunctions = nsRoutineLocal->alpfnFunctions + 4;
nsRoutineDns->alpfnFunctions = nsRoutineHosts->alpfnFunctions + 4;
nsRoutineNetbt->alpfnFunctions = nsRoutineDns->alpfnFunctions + 4;
//
// Fill in each of the NS_ROUTINE structures.
//
nsRoutineLocal->dwFunctionCount = 4;
nsRoutineLocal->alpfnFunctions[NSPAPI_GET_ADDRESS_BY_NAME] =
(LPFN_NSPAPI)TcpipLocalGetAddressByName;
nsRoutineLocal->alpfnFunctions[NSPAPI_GET_SERVICE] = NULL;
nsRoutineLocal->alpfnFunctions[NSPAPI_SET_SERVICE] =
(LPFN_NSPAPI)TcpipLocalSetService;
nsRoutineLocal->alpfnFunctions[3] = NULL;
nsRoutineLocal->dwNameSpace = NS_TCPIP_LOCAL;
nsRoutineLocal->dwPriority = LocalPriority;
nsRoutineHosts->dwFunctionCount = 4;
nsRoutineHosts->alpfnFunctions[NSPAPI_GET_ADDRESS_BY_NAME] =
(LPFN_NSPAPI)TcpipHostsGetAddressByName;
nsRoutineHosts->alpfnFunctions[NSPAPI_GET_SERVICE] = NULL;
nsRoutineHosts->alpfnFunctions[NSPAPI_SET_SERVICE] = NULL;
nsRoutineHosts->alpfnFunctions[3] = NULL;
nsRoutineHosts->dwNameSpace = NS_TCPIP_HOSTS;
nsRoutineHosts->dwPriority = HostsPriority;
nsRoutineDns->dwFunctionCount = 4;
nsRoutineDns->alpfnFunctions[NSPAPI_GET_ADDRESS_BY_NAME] =
(LPFN_NSPAPI)TcpipDnsGetAddressByName;
nsRoutineDns->alpfnFunctions[NSPAPI_GET_SERVICE] = NULL;
nsRoutineDns->alpfnFunctions[NSPAPI_SET_SERVICE] = NULL;
nsRoutineDns->alpfnFunctions[3] = NULL;
nsRoutineDns->dwNameSpace = NS_DNS;
nsRoutineDns->dwPriority = DnsPriority;
nsRoutineNetbt->dwFunctionCount = 4;
nsRoutineNetbt->alpfnFunctions[NSPAPI_GET_ADDRESS_BY_NAME] =
(LPFN_NSPAPI)TcpipNetbtGetAddressByName;
nsRoutineNetbt->alpfnFunctions[NSPAPI_GET_SERVICE] = NULL;
nsRoutineNetbt->alpfnFunctions[NSPAPI_SET_SERVICE] = NULL;
nsRoutineNetbt->alpfnFunctions[3] = NULL;
nsRoutineNetbt->dwNameSpace = NS_NETBT;
nsRoutineNetbt->dwPriority = NetbtPriority;
return 4;
} // NpLoadNameSpaces
VOID
ReadPriorities (
VOID
)
{
HKEY tcpipKey;
INT error;
DWORD entryLength = sizeof(DWORD);
DWORD type;
//
// First open the TCPIP network provider key.
//
error = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
TCPIP_SERVICE_PROVIDER_KEY,
0,
KEY_READ,
&tcpipKey
);
if ( error != NO_ERROR ) {
return;
}
//
// Read each of the priority values. If any of these fails, we'll
// just go with the default values.
//
RegQueryValueEx(
tcpipKey,
TEXT("LocalPriority"),
NULL,
&type,
(PVOID)&LocalPriority,
&entryLength
);
RegQueryValueEx(
tcpipKey,
TEXT("HostsPriority"),
NULL,
&type,
(PVOID)&HostsPriority,
&entryLength
);
RegQueryValueEx(
tcpipKey,
TEXT("DnsPriority"),
NULL,
&type,
(PVOID)&DnsPriority,
&entryLength
);
RegQueryValueEx(
tcpipKey,
TEXT("NetbtPriority"),
NULL,
&type,
(PVOID)&NetbtPriority,
&entryLength
);
#if defined(HOSTENT_CACHING_ENABLED)
RegQueryValueEx(
tcpipKey,
TEXT("MaxHostentCacheSize"),
NULL,
&type,
(PVOID)&MaxHostentCacheSize,
&entryLength
);
#endif
RegCloseKey( tcpipKey );
return;
} // ReadPriorities
INT
APIENTRY
TcpipLocalGetAddressByName (
IN LPGUID lpServiceType,
IN LPTSTR lpServiceName,
IN LPINT lpiProtocols,
IN DWORD dwResolution,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPTSTR lpAliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN HANDLE hCancellationEvent
)
{
WORD port;
INT error;
BOOL isTcp;
PHOSTENT hostEntry;
DWORD count;
PSTR hostName;
PTSTR compareHostName;
#if defined(UNICODE)
UNICODE_STRING unicodeHostName;
ANSI_STRING ansiHostName;
#endif
NTSTATUS status;
if(!SockEnterApi(FALSE, TRUE, TRUE))
{
return(0);
}
//
// Find the port number for this service. We do this before other
// resolutions to save time in case this fails.
//
error = GuidToPort( lpServiceType, lpiProtocols, &port, &isTcp );
if ( error != NO_ERROR ) {
return 0;
}
//
// If this is a service resolution, we don't actually want to do a
// lookup. Just fill in the appropriate bind address immediately.
//
if ( (dwResolution & RES_SERVICE) != 0 ) {
return ServiceCsaddr( port, isTcp, lpCsaddrBuffer, lpdwBufferLength );
}
//
// Check whether we have information for this name cached. Note
// that we don't look in the cache if RES_FIND_MULTIPLE is set since
// we'll be going to all the providers anyway and the info returned
// here will be extraneous and could cause problems with
// lpdwBufferLength if the app makes multiple calls for the same
// data.
//
#if defined(HOSTENT_CACHING_ENABLED)
if ( (dwResolution & RES_FIND_MULTIPLE) == 0 &&
CurrentHostentCacheSize > 0 ) {
//
// Get the host name in ansi for cache comparisons.
//
#if defined(UNICODE)
RtlInitUnicodeString( &unicodeHostName, lpServiceName );
status = RtlUnicodeStringToAnsiString( &ansiHostName, &unicodeHostName, TRUE );
#else
status = STATUS_SUCCESS;
#endif
if ( NT_SUCCESS(status) ) {
//
// We hold the hostent cache lock until we're completely
// done with any returned hostent. This prevents another
// thread from screwing with the list or the returned
// hostent while we're processing.
//
SockAcquireGlobalLockExclusive( );
//
// Attempt to find the name in the hostent cache.
//
#if defined(UNICODE)
hostEntry = QueryHostentCache( ansiHostName.Buffer, 0 );
RtlFreeAnsiString( &ansiHostName );
#else
hostEntry = QueryHostentCache(lpServiceName, 0);
#endif
//
// If we found it, copy it to the user buffer and return.
//
if ( hostEntry != NULL ) {
error = HostentToCsaddr(
hostEntry,
port,
isTcp,
lpCsaddrBuffer,
lpdwBufferLength,
lpAliasBuffer,
lpdwAliasBufferLength,
&count
);
SockReleaseGlobalLock( );
if ( error != NO_ERROR ) {
return 0;
}
return count;
}
SockReleaseGlobalLock( );
}
}
#endif // defined(HOSTENT_CACHING_ENABLED)
//
// If the service name is present and equal to "localhost" or
// "loopback", return 127.0.0.1.
//
if ( lpServiceName != NULL &&
(_tcscmp( lpServiceName, TEXT("localhost") ) == 0 ||
_tcscmp( lpServiceName, TEXT("loopback") ) == 0) ) {
hostEntry = localhostent( );
if ( hostEntry == NULL ) {
return 0;
}
error = HostentToCsaddr(
hostEntry,
port,
isTcp,
lpCsaddrBuffer,
lpdwBufferLength,
lpAliasBuffer,
lpdwAliasBufferLength,
&count
);
if ( error != NO_ERROR ) {
return 0;
}
return count;
}
//
// If the service name is NULL and the caller is requesting info on
// DNS addresses, return information on the DNS server IP addresses
// that we know about.
//
if ( lpServiceName == NULL && port == DNS_PORT ) {
hostEntry = dnshostent( );
if ( hostEntry == NULL ) {
return 0;
}
error = HostentToCsaddr(
hostEntry,
port,
isTcp,
lpCsaddrBuffer,
lpdwBufferLength,
lpAliasBuffer,
lpdwAliasBufferLength,
&count
);
if ( error != NO_ERROR ) {
return 0;
}
return count;
}
//
// Check whether the service name is the same as the local host
// name. Note that we consider lpServiceName == NULL to be the
// local host.
//
hostName = ALLOCATE_HEAP( 256 );
if ( hostName == NULL ) {
return 0;
}
error = gethostname( hostName, 256 );
if ( error ) {
return 0;
}
#if defined(UNICODE)
RtlInitAnsiString( &ansiHostName, hostName );
status = RtlAnsiStringToUnicodeString( &unicodeHostName, &ansiHostName, TRUE );
FREE_HEAP( hostName );
if ( !NT_SUCCESS(status) ) {
return 0;
}
compareHostName = unicodeHostName.Buffer;
#else
compareHostName = hostName;
#endif
if ( lpServiceName == NULL ||
_tcscmp( lpServiceName, compareHostName ) == 0 ) {
#if defined(UNICODE)
RtlFreeUnicodeString( &unicodeHostName );
#endif
hostEntry = myhostent( );
if ( hostEntry == NULL ) {
return 0;
}
error = HostentToCsaddr(
hostEntry,
port,
isTcp,
lpCsaddrBuffer,
lpdwBufferLength,
lpAliasBuffer,
lpdwAliasBufferLength,
&count
);
if ( error != NO_ERROR ) {
return 0;
}
return count;
}
#if defined(UNICODE)
RtlFreeUnicodeString( &unicodeHostName );
#endif
//
// If we're not inside gethostbyname(), check if the service name is
// really an IP address. We intentionally disable this for
// gethostbyname() calls because we don't want to break apps which
// depend on gethostname() not resolving IP addresses. However,
// this is a useful feature for general RNR applications.
//
if ( (dwResolution & RES_GETHOSTBYNAME) == 0 ) {
PSTR serviceString;
IN_ADDR inAddr;
HOSTENT hostEntryStruct;
PBYTE nullAlias = NULL;
PBYTE addrs[2];
#if defined(UNICODE)
RtlInitUnicodeString( &unicodeHostName, lpServiceName );
status = RtlUnicodeStringToAnsiString( &ansiHostName, &unicodeHostName, TRUE );
if ( !NT_SUCCESS(status) ) {
return 0;
}
serviceString = ansiHostName.Buffer;
#else
serviceString = lpServiceName;
#endif
//
// Attempt to translate the string into an IP address value.
//
inAddr.s_addr = inet_addr( serviceString );
if ( inAddr.s_addr != INADDR_NONE ) {
hostEntryStruct.h_name = serviceString;
hostEntryStruct.h_aliases = &nullAlias;
hostEntryStruct.h_addrtype = AF_INET;
hostEntryStruct.h_length = sizeof(DWORD);
hostEntryStruct.h_addr_list = addrs;
addrs[0] = (char *)(&inAddr);
addrs[1] = NULL;
error = HostentToCsaddr(
&hostEntryStruct,
port,
isTcp,
lpCsaddrBuffer,
lpdwBufferLength,
lpAliasBuffer,
lpdwAliasBufferLength,
&count
);
if ( error != NO_ERROR ) {
return 0;
}
return count;
}
}
return 0;
} // TcpipLocalGetAddressByName
INT
APIENTRY
TcpipHostsGetAddressByName (
IN LPGUID lpServiceType,
IN LPTSTR lpServiceName,
IN LPINT lpiProtocols,
IN DWORD dwResolution,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPTSTR lpAliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN HANDLE hCancellationEvent
)
{
WORD port;
INT error;
BOOL isTcp;
PHOSTENT hostEntry;
DWORD count;
PSTR hostName;
#if defined(CHICAGO)
LPSOCK_THREAD pThread;
#endif
if(!SockEnterApi(FALSE, TRUE, TRUE))
{
return(0);
}
//
// If this is a service resolution, don't do anything here. Only the
// TCP/IP local NSP will handle RES_SERVICE requests. This prevents
// getting back multiple TCP/IP addresses to bind to, one from each
// TCP/IP NSP.
//
if ( (dwResolution & RES_SERVICE) != 0 ) {
return 0;
}
//
// Find the port number for this service. We do this before other
// resolutions to save time in case this fails.
//
error = GuidToPort( lpServiceType, lpiProtocols, &port, &isTcp );
if ( error != NO_ERROR ) {
return 0;
}
//
// Convert the service name to ANSI.
//
hostName = GetAnsiName( lpServiceName );
if ( hostName == NULL ) {
return 0;
}
//
// Attempt to get a host entry from the hosts file lookup routine.
//
hostEntry = _gethtbyname( hostName );
FREE_HEAP( hostName );
if ( hostEntry == NULL ) {
return 0;
}
error = HostentToCsaddr(
hostEntry,
port,
isTcp,
lpCsaddrBuffer,
lpdwBufferLength,
lpAliasBuffer,
lpdwAliasBufferLength,
&count
);
if ( error != NO_ERROR ) {
return 0;
}
return count;
} // TcpipHostsGetAddressByName
INT
APIENTRY
TcpipDnsGetAddressByName (
IN LPGUID lpServiceType,
IN LPTSTR lpServiceName,
IN LPINT lpiProtocols,
IN DWORD dwResolution,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPTSTR lpAliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN HANDLE hCancellationEvent
)
{
WORD port;
INT error;
BOOL isTcp;
PHOSTENT hostEntry;
INT count;
querybuf queryBuffer;
INT ttl;
PSTR hostName;
#if defined(CHICAGO)
LPSOCK_THREAD pThread;
#endif
if(!SockEnterApi(FALSE, TRUE, TRUE))
{
return(0);
}
//
// If this is a service resolution, don't do anything here. Only the
// TCP/IP local NSP will handle RES_SERVICE requests. This prevents
// getting back multiple TCP/IP addresses to bind to, one from each
// TCP/IP NSP.
//
if ( (dwResolution & RES_SERVICE) != 0 ) {
return 0;
}
//
// Initialize the resolver.
//
if (res_init() == -1) {
SetLastError( ERROR_FILE_NOT_FOUND );
return SOCKET_ERROR;
}
//
// Find the port number for this service. We do this before other
// resolutions to save time in case this fails.
//
error = GuidToPort( lpServiceType, lpiProtocols, &port, &isTcp );
if ( error != NO_ERROR ) {
return 0;
}
//
// Convert the service name to ANSI.
//
hostName = GetAnsiName( lpServiceName );
if ( hostName == NULL ) {
return 0;
}
//
// Attempt to get a host entry from the DNR lookup routines.
//
hostEntry = NULL;
count = res_search( hostName, C_IN, T_A,
queryBuffer.buf, sizeof(queryBuffer) );
if ( count >= 0 ) {
#if defined(CHICAGO)
hostEntry = getanswer( pThread, &queryBuffer, &ttl, count, 0 );
#else
hostEntry = getanswer( &queryBuffer, &ttl, count, 0 );
#endif
}
FREE_HEAP( hostName );
if ( hostEntry == NULL ) {
return 0;
}
error = HostentToCsaddr(
hostEntry,
port,
isTcp,
lpCsaddrBuffer,
lpdwBufferLength,
lpAliasBuffer,
lpdwAliasBufferLength,
&count
);
if ( error != NO_ERROR ) {
return 0;
}
//
// Store the host's information in our cache.
//
#if defined(HOSTENT_CACHING_ENABLED)
CacheHostent( hostEntry, ttl );
#endif
return count;
} // TcpipDnsGetAddressByName
INT
APIENTRY
TcpipNetbtGetAddressByName (
IN LPGUID lpServiceType,
IN LPTSTR lpServiceName,
IN LPINT lpiProtocols,
IN DWORD dwResolution,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPTSTR lpAliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN HANDLE hCancellationEvent
)
{
WORD port;
INT error;
BOOL isTcp;
HOSTENT hostEntry;
DWORD count;
DWORD ipAddress;
PVOID addressPtrs[2];
PSTR hostName;
#if defined(CHICAGO)
LPSOCK_THREAD pThread;
#endif
//
// If this is a service resolution, don't do anything here. Only the
// TCP/IP local NSP will handle RES_SERVICE requests. This prevents
// getting back multiple TCP/IP addresses to bind to, one from each
// TCP/IP NSP.
//
if(!SockEnterApi(FALSE, TRUE, TRUE))
{
return(0);
}
if ( (dwResolution & RES_SERVICE) != 0 ) {
return 0;
}
//
// If we're not supposed to do WINS name resolution for this thread,
// stop.
//
if ( SockDisableWinsNameResolution ) {
return 0;
}
//
// Find the port number for this service. We do this before other
// resolutions to save time in case this fails.
//
error = GuidToPort( lpServiceType, lpiProtocols, &port, &isTcp );
if ( error != NO_ERROR ) {
return 0;
}
//
// Convert the service name to ANSI.
//
hostName = GetAnsiName( lpServiceName );
if ( hostName == NULL ) {
return 0;
}
//
// Attempt to get the IP address from the WINS client.
//
ipAddress = SockNbtResolveName( hostName );
if ( ipAddress == INADDR_NONE ) {
FREE_HEAP( hostName );
return 0;
}
//
// Fake up a hostent and convert to a CSADDR structure.
//
hostEntry.h_name = hostName;
hostEntry.h_addr_list = (PCHAR *)addressPtrs;
addressPtrs[0] = &ipAddress;
addressPtrs[1] = NULL;
hostEntry.h_length = sizeof (unsigned long);
hostEntry.h_addrtype = AF_INET;
hostEntry.h_aliases = host_aliases;
host_aliases[0] = NULL;
error = HostentToCsaddr(
&hostEntry,
port,
isTcp,
lpCsaddrBuffer,
lpdwBufferLength,
lpAliasBuffer,
lpdwAliasBufferLength,
&count
);
if ( error != NO_ERROR ) {
FREE_HEAP( hostName );
return 0;
}
//
// Store the host's information in our cache with a 10-minute timeout.
//
#if defined(HOSTENT_CACHING_ENABLED)
CacheHostent( &hostEntry, 60*10 );
#endif
FREE_HEAP( hostName );
return count;
} // TcpipNetbtGetAddressByName
DWORD
APIENTRY
TcpipLocalSetService (
IN DWORD dwOperation,
IN DWORD dwFlags,
IN BOOL fUnicodeBlob,
IN LPSERVICE_INFO lpServiceInfo
)
{
DWORD err;
HKEY hkey = NULL;
HKEY hkeyType = NULL;
SERVICE_TYPE_INFO *pSvcTypeInfo = (SERVICE_TYPE_INFO *)
lpServiceInfo->ServiceSpecificInfo.pBlobData;
LPTSTR pszSvcTypeName;
DWORD i;
PSERVICE_TYPE_VALUE pVal;
#if defined(UNICODE)
UNICODE_STRING uniStr;
#endif
//
// We need to take action only on SERVICE_ADD_TYPE. The rest are
// meaningless for the static name spaces we support.
//
if ( dwOperation != SERVICE_ADD_TYPE ) {
return NO_ERROR;
}
//
// Get the new service type name
//
#if defined(UNICODE)
if ( fUnicodeBlob ) {
pszSvcTypeName = (LPWSTR) (((LPBYTE) pSvcTypeInfo) +
pSvcTypeInfo->dwTypeNameOffset );
} else {
ANSI_STRING ansiStr;
RtlInitAnsiString( &ansiStr,
(LPSTR) (((LPBYTE) pSvcTypeInfo) +
pSvcTypeInfo->dwTypeNameOffset ));
err = RtlAnsiStringToUnicodeString( &uniStr, &ansiStr, TRUE );
if ( err )
return err;
pszSvcTypeName = uniStr.Buffer;
}
#else
pszSvcTypeName = (LPSTR) (((LPBYTE) pSvcTypeInfo) +
pSvcTypeInfo->dwTypeNameOffset );
#endif
//
// If the service type name is an empty string, return error.
//
if ( ( pSvcTypeInfo->dwTypeNameOffset == 0 ) ||
( pszSvcTypeName == NULL ) ||
( *pszSvcTypeName == 0 ) ) {
err = ERROR_INVALID_PARAMETER;
goto CleanExit;
}
//
// The following keys should have already been created
//
err = REG_OPEN_KEY_EX(
HKEY_LOCAL_MACHINE,
NSP_SERVICE_KEY_NAME,
0,
KEY_READ | KEY_WRITE,
&hkey
);
if ( err ) {
goto CleanExit;
}
err = REG_OPEN_KEY_EX(
hkey,
pszSvcTypeName,
0,
KEY_READ | KEY_WRITE,
&hkeyType
);
if ( err ) {
goto CleanExit;
}
//
// Loop through all values in the specific and add them one by one
// to the registry if it belongs to our name space
//
for ( i = 0, pVal = pSvcTypeInfo->Values;
i < pSvcTypeInfo->dwValueCount;
i++, pVal++ )
{
//
// Skip over values not aimed at one of the TCPIP name spaces.
//
if ( pVal->dwNameSpace != NS_TCPIP_LOCAL &&
pVal->dwNameSpace != NS_TCPIP_HOSTS &&
pVal->dwNameSpace != NS_DNS &&
pVal->dwNameSpace != NS_NETBT ) {
continue;
}
if ( fUnicodeBlob ) {
err = RegSetValueExW(
hkeyType,
(LPWSTR) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueNameOffset),
0,
pVal->dwValueType,
(LPBYTE) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueOffset),
pVal->dwValueSize
);
} else {
err = RegSetValueExA(
hkeyType,
(LPSTR) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueNameOffset),
0,
pVal->dwValueType,
(LPBYTE) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueOffset),
pVal->dwValueSize
);
}
}
CleanExit:
#if defined(UNICODE)
if ( !fUnicodeBlob ) {
RtlFreeUnicodeString( &uniStr );
}
#endif
if ( hkeyType != NULL ) {
RegCloseKey( hkeyType );
}
if ( hkey != NULL ) {
RegCloseKey( hkey );
}
return err;
} // TcpipLocalSetService
INT
GuidToPort (
IN LPGUID lpServiceType,
IN LPINT lpiProtocols,
OUT LPWORD Port,
OUT LPBOOL IsTcp
)
{
TCHAR nameBuffer[256];
TCHAR keyNameBuffer[256];
BOOL isTcp;
BOOL isUdp;
BOOL guidIsTcp;
DWORD i;
BOOL ianaGuid;
HKEY serviceKey;
DWORD regPort;
DWORD type;
INT err;
DWORD bytesRequired;
//
// Determine whether they want TCP, UDP, or both. If there was no
// protocols list passed in, they want both. Otherwise, they must
// specify on or both manually.
//
isTcp = FALSE;
isUdp = FALSE;
if ( !ARGUMENT_PRESENT( lpiProtocols ) ) {
isTcp = TRUE;
isUdp = TRUE;
} else {
for ( i = 0; lpiProtocols[i] != 0; i++ ) {
if ( lpiProtocols[i] == IPPROTO_TCP ) {
isTcp = TRUE;
}
if ( lpiProtocols[i] == IPPROTO_UDP ) {
isUdp = TRUE;
}
}
}
//
// If they want neither TCP nor UDP, bail.
//
if ( !isTcp && !isUdp ) {
return -1;
}
//
// If this is the special hostname GUID, we don't need to find a
// port.
//
if ( GuidEqual( lpServiceType, &HostnameGuid ) ) {
*Port = 0;
*IsTcp = TRUE;
return NO_ERROR;
}
//
// If this is an IANA GUID, shortcut to the answer.
//
ianaGuid = FALSE;
if ( IS_SVCID_TCP( lpServiceType ) ) {
guidIsTcp = TRUE;
ianaGuid = TRUE;
*Port = htons(PORT_FROM_SVCID_TCP( lpServiceType ));
}
if ( IS_SVCID_UDP( lpServiceType ) ) {
guidIsTcp = FALSE;
ianaGuid = TRUE;
*Port = htons(PORT_FROM_SVCID_UDP( lpServiceType ));
}
if ( ianaGuid ) {
//
// If an incorrect protocol type was specified for the GUID, fail.
//
if ( (guidIsTcp && !isTcp) || (!guidIsTcp && !isUdp) ) {
return -1;
}
*IsTcp = guidIsTcp;
return NO_ERROR;
}
//
// Convert the GUID into a service name. We'll first check whether
// port information is in the registry. An application/service
// writes this information to the registry by using SetService()
// with SERVICE_ADD_TYPE.
//
err = GetNameByType( lpServiceType, nameBuffer, sizeof(nameBuffer) );
if ( err != NO_ERROR ) {
return -1;
}
//
// Attempt to open the service type key.
//
_tcscpy( keyNameBuffer, NSP_SERVICE_KEY_NAME TEXT("\\") );
_tcscat( keyNameBuffer, nameBuffer );
err = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
keyNameBuffer,
0,
KEY_READ,
&serviceKey
);
if ( err != NO_ERROR ) {
return -1;
}
//
// Attempt to read the "TcpPort" or "UdpPort" values from this key.
// Note that we sort of ignore errors here; we act based on whether
// the regPort local variable changes from 0xFFFFFFFF.
//
// Also, we don't have any support for a single GUID which supports
// both TCP and UDP. This currently requires separate GUIDs.
//
regPort = 0xFFFFFFFF;
guidIsTcp = TRUE;
bytesRequired = sizeof(regPort);
if ( isTcp ) {
RegQueryValueEx(
serviceKey,
TEXT("TcpPort"),
NULL,
&type,
(PVOID)&regPort,
&bytesRequired
);
}
if ( regPort == 0xFFFFFFFF && isUdp ) {
RegQueryValueEx(
serviceKey,
TEXT("UdpPort"),
NULL,
&type,
(PVOID)&regPort,
&bytesRequired
);
guidIsTcp = FALSE;
}
RegCloseKey( serviceKey );
//
// If we got a hit, then we're all set. Use this port.
//
if ( regPort != 0xFFFFFFFF ) {
*IsTcp = guidIsTcp;
*Port = htons( (WORD)regPort );
return NO_ERROR;
}
//
// We coultn't find a port number for this service type. Fail.
//
return -1;
} // GuidToPort
INT
HostentToCsaddr (
IN PHOSTENT HostEntry,
IN WORD Port,
IN BOOL IsTcp,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPTSTR lpAliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN OUT LPDWORD Count
)
{
DWORD count;
DWORD i;
DWORD requiredBufferLength;
PSOCKADDR_IN sockaddrIn;
PCSADDR_INFO csaddrInfo;
DWORD aliasBytesUsed;
//
// Count the number of IP addresses in the hostent.
//
for ( count = 0; HostEntry->h_addr_list[count] != NULL; count++ );
//
// Make sure that the buffer is large enough to hold all the entries
// which will be necessary.
//
requiredBufferLength = count * (sizeof(CSADDR_INFO) + 2*sizeof(SOCKADDR_IN));
if ( *lpdwBufferLength < requiredBufferLength ) {
*lpdwBufferLength = requiredBufferLength;
return (DWORD)-1;
}
*lpdwBufferLength = requiredBufferLength;
//
// For each IP address, fill in the user buffer with one entry.
//
sockaddrIn = (PSOCKADDR_IN)((PCSADDR_INFO)lpCsaddrBuffer + count);
csaddrInfo = lpCsaddrBuffer;
for ( i = 0; i < count; i++, csaddrInfo++, sockaddrIn++ ) {
//
// First fill in the local address. It should remain basically
// all zeros except for the family so that it is a "wildcard"
// address for binding.
//
RtlZeroMemory( csaddrInfo, sizeof(*csaddrInfo) );
csaddrInfo->LocalAddr.lpSockaddr = (LPSOCKADDR)sockaddrIn;
csaddrInfo->LocalAddr.iSockaddrLength = sizeof(SOCKADDR_IN);
RtlZeroMemory( sockaddrIn, sizeof(*sockaddrIn) );
sockaddrIn->sin_family = AF_INET;
//
// Now the remote address.
//
sockaddrIn++;
csaddrInfo->RemoteAddr.lpSockaddr = (PSOCKADDR)( sockaddrIn );
csaddrInfo->RemoteAddr.iSockaddrLength = sizeof(SOCKADDR_IN);
sockaddrIn = (PSOCKADDR_IN)csaddrInfo->RemoteAddr.lpSockaddr;
RtlZeroMemory( sockaddrIn, sizeof(*sockaddrIn) );
sockaddrIn->sin_family = AF_INET;
//
// Fill in the remote address with the actual address, both port
// and IP address.
//
sockaddrIn->sin_port = Port;
sockaddrIn->sin_addr.s_addr =
*((long *)(HostEntry->h_addr_list[i]));
//
// Lastly, fill in the protocol information.
//
if ( IsTcp ) {
csaddrInfo->iSocketType = SOCK_STREAM;
csaddrInfo->iProtocol = IPPROTO_TCP;
} else {
csaddrInfo->iSocketType = SOCK_DGRAM;
csaddrInfo->iProtocol = IPPROTO_UDP;
}
}
//
// Copy the aliases to the alias buffer. The "official" name from the
// hostent goes as the first alias, then the actual aliases.
//
if ( ARGUMENT_PRESENT( lpAliasBuffer ) &&
ARGUMENT_PRESENT( lpdwAliasBufferLength ) ) {
//
// Copy over the official name first.
//
aliasBytesUsed = 0;
CopyToAliasBuffer(
HostEntry->h_name,
lpAliasBuffer,
lpdwAliasBufferLength,
&aliasBytesUsed
);
if ( HostEntry->h_aliases != NULL ) {
for ( i = 0; HostEntry->h_aliases[i] != NULL; i++ ) {
CopyToAliasBuffer(
HostEntry->h_aliases[i],
lpAliasBuffer,
lpdwAliasBufferLength,
&aliasBytesUsed
);
}
}
//
// Set up the final zero-terminator on the alias list.
//
aliasBytesUsed += sizeof(TCHAR);
if ( aliasBytesUsed <= *lpdwAliasBufferLength ) {
lpAliasBuffer[(aliasBytesUsed / sizeof(TCHAR)) - 1] = L'\0';
}
//
// Tell the caller how much of the alias buffer we used.
//
*lpdwAliasBufferLength = aliasBytesUsed;
}
*Count = count;
return NO_ERROR;
} // HostentToCsaddr
VOID
CopyToAliasBuffer (
IN PSTR Alias,
IN OUT LPTSTR AliasBuffer,
IN OUT LPDWORD lpdwAliasBufferLength,
IN OUT LPDWORD AliasBytesUsed
)
{
#if defined(UNICODE)
UNICODE_STRING unicodeString;
ANSI_STRING ansiString;
#endif
//
// Point to the first unused byte in the alias buffer.
//
AliasBuffer = (LPTSTR)( (PBYTE)AliasBuffer + *AliasBytesUsed );
//
// Initialize string structures for the alias.
//
#if defined(UNICODE)
RtlInitAnsiString( &ansiString, Alias );
unicodeString.Buffer = AliasBuffer;
unicodeString.MaximumLength =
(USHORT)(*lpdwAliasBufferLength - *AliasBytesUsed);
#endif
//
// Make sure that there is enough room left in the alias buffer.
//
*AliasBytesUsed += (strlen( Alias ) + 1) * sizeof(TCHAR);
if ( *AliasBytesUsed > *lpdwAliasBufferLength ) {
return;
}
//
// Convert the ANSI name to Unicode and copy it to the alias buffer.
//
#if defined(UNICODE)
RtlAnsiStringToUnicodeString( &unicodeString, &ansiString, FALSE );
#else
_tcscpy( AliasBuffer, Alias );
#endif
return;
} // CopyToAliasBuffer
#if defined(HOSTENT_CACHING_ENABLED)
VOID
CacheHostent (
IN PHOSTENT HostEntry,
IN INT Ttl
)
{
MY_LARGE_INTEGER currentTime;
MY_LARGE_INTEGER liTtl;
DWORD bytesRequired;
PHOSTENT_CACHE_ENTRY cacheEntry;
PLIST_ENTRY listEntry;
PHOSTENT_CACHE_ENTRY testCacheEntry;
PHOSTENT_CACHE_ENTRY oldestCacheEntry;
//
// If the TTL is 0, do not cache the entry.
//
if ( ( Ttl <= 0 ) || ( MaxHostentCacheSize == 0 ) ) {
return;
}
//
// Get the current time and convert the TTL to 64 bit time.
//
#if defined(CHICAGO)
currentTime = time(NULL);
liTtl = Ttl;
#else
NtQuerySystemTime( &currentTime );
liTtl = RtlEnlargedIntegerMultiply( Ttl, 10*1000*1000 );
#endif
//
// Allocate space to hold the hostent information.
//
bytesRequired = sizeof(*cacheEntry) + BytesInHostent( HostEntry ) + 20;
cacheEntry = ALLOCATE_HEAP( bytesRequired );
if ( cacheEntry == NULL ) {
return;
}
//
// Set up the cache entry.
//
cacheEntry->LastAccessTime = currentTime;
QUADPART(cacheEntry->ExpirationTime) = QUADPART(currentTime) + QUADPART(liTtl);
CopyHostentToBuffer(
(char FAR *)&cacheEntry->HostEntry,
bytesRequired - sizeof(*cacheEntry),
HostEntry
);
//
// Acquire the global lock exclusively to protect our lists and
// counts, and test whether we're at the limit of the caching we'll
// do.
//
SockAcquireGlobalLockExclusive( );
if ( CurrentHostentCacheSize < MaxHostentCacheSize ) {
CurrentHostentCacheSize++;
} else {
//
// We're at our limit for cached entries. Remove the oldest
// entry from the cache.
//
oldestCacheEntry = NULL;
for ( listEntry = HostentCacheListHead.Flink;
listEntry != &HostentCacheListHead;
listEntry = listEntry->Flink ) {
testCacheEntry = CONTAINING_RECORD(
listEntry,
HOSTENT_CACHE_ENTRY,
HostentListEntry
);
if ( oldestCacheEntry == NULL ||
QUADPART(testCacheEntry->LastAccessTime) <
QUADPART(oldestCacheEntry->LastAccessTime) ) {
oldestCacheEntry = testCacheEntry;
}
}
RemoveEntryList( &oldestCacheEntry->HostentListEntry );
FREE_HEAP( oldestCacheEntry );
}
//
// Place the new entry at the front of the global list and return;
//
InsertHeadList( &HostentCacheListHead, &cacheEntry->HostentListEntry );
SockReleaseGlobalLock( );
return;
} // CacheHostent
PHOSTENT
QueryHostentCache (
IN LPSTR Name OPTIONAL,
IN DWORD IpAddress OPTIONAL
)
{
PLIST_ENTRY listEntry;
PHOSTENT_CACHE_ENTRY testCacheEntry;
DWORD i;
MY_LARGE_INTEGER currentTime;
PHOSTENT hostEntry;
//
// *** It is assumed that this routine is called while the caller
// holds the appropriate global cache lock!
//
//
// First get the current system time. We'll use this to reset the
// LastAccessTime if we find a hit.
//
#if defined(CHICAGO)
currentTime = time(NULL);
#else
NtQuerySystemTime( &currentTime );
#endif
//
// Walk the host entry cache. As soon as we find a match, quit
// walking the list.
//
for ( listEntry = HostentCacheListHead.Flink;
listEntry != &HostentCacheListHead; ) {
testCacheEntry = CONTAINING_RECORD(
listEntry,
HOSTENT_CACHE_ENTRY,
HostentListEntry
);
hostEntry = &testCacheEntry->HostEntry;
//
// If this entry has expired, remove it from the list.
//
if ( QUADPART(currentTime) > QUADPART(testCacheEntry->ExpirationTime) ) {
CurrentHostentCacheSize--;
RemoveEntryList( &testCacheEntry->HostentListEntry );
listEntry = listEntry->Flink;
FREE_HEAP( testCacheEntry );
continue;
}
//
// First check for a matching name. A match on either the
// primary name or any of the aliases results in a hit.
//
if ( ARGUMENT_PRESENT( Name ) ) {
if ( LSTRCMPI( Name, hostEntry->h_name ) == 0 ) {
testCacheEntry->LastAccessTime = currentTime;
return hostEntry;
}
for ( i = 0; hostEntry->h_aliases[i] != NULL; i++ ) {
if ( LSTRCMPI( Name, hostEntry->h_aliases[i] ) == 0 ) {
testCacheEntry->LastAccessTime = currentTime;
return hostEntry;
}
}
}
//
// Now check for a match against any of the IP addresses in
// the hostent.
//
if ( IpAddress != 0 ) {
for ( i = 0; hostEntry->h_addr_list[i] != NULL; i++ ) {
if ( IpAddress == *(PDWORD)hostEntry->h_addr_list[i] ) {
testCacheEntry->LastAccessTime = currentTime;
return hostEntry;
}
}
}
listEntry = listEntry->Flink;
}
//
// We didn't find a match in the hostent cache.
//
return NULL;
} // QueryHostentCache
#endif // defined(HOSTENT_CACHING_ENABLED)
INT
ServiceCsaddr (
IN WORD Port,
IN BOOL IsTcp,
IN OUT LPVOID lpCsaddrBuffer,
IN OUT LPDWORD lpdwBufferLength
)
{
DWORD requiredBufferLength;
PCSADDR_INFO csaddrInfo;
PSOCKADDR_IN sockaddrIn;
//
// We're going to return exactly one sockaddr and csaddr structure.
// Make sure that the user buffer is large enough.
//
requiredBufferLength = sizeof(CSADDR_INFO) + 2*sizeof(SOCKADDR_IN);
if ( *lpdwBufferLength < requiredBufferLength ) {
*lpdwBufferLength = requiredBufferLength;
return (DWORD)-1;
}
*lpdwBufferLength = requiredBufferLength;
//
// Fill in the appropriate information. Set the local sockaddr's
// port to the port for the service, and leave the remote's port
// zero. All IP addresses remain INADDR_ANY (0).
//
csaddrInfo = lpCsaddrBuffer;
RtlZeroMemory( csaddrInfo, sizeof(*csaddrInfo) );
sockaddrIn = (PSOCKADDR_IN)(csaddrInfo + 1);
csaddrInfo->LocalAddr.lpSockaddr = (LPSOCKADDR)sockaddrIn;
csaddrInfo->LocalAddr.iSockaddrLength = sizeof(SOCKADDR_IN);
if ( IsTcp ) {
csaddrInfo->iSocketType = SOCK_STREAM;
csaddrInfo->iProtocol = IPPROTO_TCP;
} else {
csaddrInfo->iSocketType = SOCK_DGRAM;
csaddrInfo->iProtocol = IPPROTO_UDP;
}
RtlZeroMemory( sockaddrIn, sizeof(*sockaddrIn) );
sockaddrIn->sin_family = AF_INET;
sockaddrIn->sin_port = Port;
sockaddrIn += 1;
csaddrInfo->RemoteAddr.lpSockaddr = (LPSOCKADDR)sockaddrIn;
csaddrInfo->RemoteAddr.iSockaddrLength = sizeof(SOCKADDR_IN);
RtlZeroMemory( sockaddrIn, sizeof(*sockaddrIn) );
sockaddrIn->sin_family = AF_INET;
//
// Return a count of one entry.
//
return 1;
} // ServiceCsaddr
#if defined(CHICAGO)
INT
WSHEnumProtocols (
IN LPINT lpiProtocols,
IN LPSTR lpTransportKeyName,
IN OUT LPVOID lpProtocolBuffer,
IN OUT LPDWORD lpdwBufferLength
)
{
DWORD bytesRequired;
PPROTOCOL_INFO11 tcpProtocolInfo;
PPROTOCOL_INFO11 udpProtocolInfo;
BOOL useTcp = FALSE;
BOOL useUdp = FALSE;
DWORD i;
lpTransportKeyName; // Avoid compiler warnings.
//
// Make sure that the caller cares about TCP and/or UDP.
//
if ( ARGUMENT_PRESENT( lpiProtocols ) ) {
for ( i = 0; lpiProtocols[i] != 0; i++ ) {
if ( lpiProtocols[i] == IPPROTO_TCP ) {
useTcp = TRUE;
}
if ( lpiProtocols[i] == IPPROTO_UDP ) {
useUdp = TRUE;
}
}
} else {
useTcp = TRUE;
useUdp = TRUE;
}
if ( !useTcp && !useUdp ) {
*lpdwBufferLength = 0;
return 0;
}
//
// Make sure that the caller has specified a sufficiently large
// buffer.
//
bytesRequired = (sizeof(PROTOCOL_INFO11) * 2) +
( (lstrlen( TCP_NAME ) + 1) * sizeof(TCHAR)) +
( (lstrlen( UDP_NAME ) + 1) * sizeof(TCHAR));
if ( bytesRequired > *lpdwBufferLength ) {
*lpdwBufferLength = bytesRequired;
return -1;
}
//
// Fill in TCP info, if requested.
//
if ( useTcp ) {
tcpProtocolInfo = lpProtocolBuffer;
tcpProtocolInfo->dwServiceFlags = XP_GUARANTEED_DELIVERY |
XP_GUARANTEED_ORDER |
XP_GRACEFUL_CLOSE |
XP_EXPEDITED_DATA |
XP_FRAGMENTATION;
tcpProtocolInfo->iAddressFamily = AF_INET;
tcpProtocolInfo->iMaxSockAddr = sizeof(SOCKADDR_IN);
tcpProtocolInfo->iMinSockAddr = sizeof(SOCKADDR_IN);
tcpProtocolInfo->iSocketType = SOCK_STREAM;
tcpProtocolInfo->iProtocol = IPPROTO_TCP;
tcpProtocolInfo->dwMessageSize = 0;
tcpProtocolInfo->lpProtocol = (LPSTR)
( (PBYTE)lpProtocolBuffer + *lpdwBufferLength -
( (lstrlen( TCP_NAME ) + 1) * sizeof(TCHAR) ) );
lstrcpy( tcpProtocolInfo->lpProtocol, TCP_NAME );
udpProtocolInfo = tcpProtocolInfo + 1;
udpProtocolInfo->lpProtocol = (LPSTR)
( (PBYTE)tcpProtocolInfo->lpProtocol -
( (lstrlen( UDP_NAME ) + 1) * sizeof(TCHAR) ) );
} else {
udpProtocolInfo = lpProtocolBuffer;
udpProtocolInfo->lpProtocol = (LPSTR)
( (PBYTE)lpProtocolBuffer + *lpdwBufferLength -
( (lstrlen( UDP_NAME ) + 1) * sizeof(TCHAR) ) );
}
//
// Fill in UDP info, if requested.
//
if ( useUdp ) {
udpProtocolInfo->dwServiceFlags = XP_CONNECTIONLESS |
XP_MESSAGE_ORIENTED |
XP_SUPPORTS_BROADCAST |
XP_SUPPORTS_MULTICAST |
XP_FRAGMENTATION;
udpProtocolInfo->iAddressFamily = AF_INET;
udpProtocolInfo->iMaxSockAddr = sizeof(SOCKADDR_IN);
udpProtocolInfo->iMinSockAddr = sizeof(SOCKADDR_IN);
udpProtocolInfo->iSocketType = SOCK_DGRAM;
udpProtocolInfo->iProtocol = IPPROTO_UDP;
udpProtocolInfo->dwMessageSize = 65535-68;
lstrcpy( udpProtocolInfo->lpProtocol, UDP_NAME );
}
*lpdwBufferLength = bytesRequired;
return (useTcp && useUdp) ? 2 : 1;
} // WSHEnumProtocols
#endif