RNRCLNT.C

/*++ 

Copyright (c) 1992 Microsoft Corporation

Module Name:

RnrClnt.c

Abstract:

Test and demonstration client for the RNR (service Registration and
Name Resolution) APIs. This is a simple client application designed
to show the basic principles involved in using the RNR APIs to _write
a protocol-independent Windows Sockets client application.

This client works by examining the protocols loaded on the machine,
looking for protocols which are reliable and stream-oriented. Then
it attempts to locate and connect to the service on these protocols.
When is has successfully connected to the service, it sends
exchanges several messages with the service and then terminates the
connection.

The OpenConnection() routine implemented herein is intended to be a
demonstration of RNR functionality commonly used in
protocol-independent clients. Application writers are encouraged to
leverage this code to assist them in writing protocol-independent
applications on top of the Windows Sockets API.


--*/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock2.h>
#include <nspapi.h>

#define DEFAULT_TRANSFER_SIZE 512
#define DEFAULT_TRANSFER_COUNT 0x10
#define DEFAULT_CONNECTION_COUNT 1
#define DEFAULT_DELAY 0

#define DEFAULT_RECEIVE_BUFFER_SIZE 4096
#define DEFAULT_SEND_BUFFER_SIZE 4096

#define MAX_PROTOCOLS 10
#define MAX_HOST_NAMES 16

WSADATA WsaData;
DWORD TransferSize = DEFAULT_TRANSFER_SIZE;
DWORD TransferCount = DEFAULT_TRANSFER_COUNT;
PCHAR IoBuffer;
DWORD RepeatCount = 1;
INT ReceiveBufferSize = DEFAULT_RECEIVE_BUFFER_SIZE;
INT SendBufferSize = DEFAULT_SEND_BUFFER_SIZE;

PCHAR RemoteName = "localhost";
PCHAR ServiceTypeName = "EchoExample";

VOID
DoEcho(
IN SOCKET s );

SOCKET
OpenConnection(
IN PTSTR ServiceName,
IN LPGUID ServiceType,
IN BOOL Reliable,
IN BOOL MessageOriented,
IN BOOL StreamOriented,
IN BOOL Connectionless,
OUT PINT ProtocolUsed );

INT
Rnr20_GetAddressByName(
IN PTSTR ServiceName,
IN LPGUID ServiceType,
IN DWORD dwNameSpace,
IN DWORD dwNumberOfProtocols,
IN LPAFPROTOCOLS lpAfpProtocols,
IN OUT LPVOID lpCSAddrInfo,
IN OUT LPDWORD lpdwBufferLength );

void _CRTAPI1
main(
int argc,
char *argv[] )
{
INT err;
DWORD i;
DWORD protocol[2];
SOCKET s;
BYTE buffer[1024];
BYTE buffer2[1024];
DWORD bytesRequired;
PPROTOCOL_INFO protocolInfo;
GUID serviceType;

//
// Initialize the Windows Sockets DLL.
//

err = WSAStartup( 0x0202, &WsaData );
if ( err == SOCKET_ERROR )
{
printf( "WSAStartup() failed: %ld\n", GetLastError( ) );
return;
}

//
// Parse command-line arguments.
//

for ( i = 1; i < (ULONG)argc != 0; i++ )
{
if ( _strnicmp( argv[i], "/name:", 6 ) == 0 )
{
RemoteName = argv[i] + 6;
}
else if ( _strnicmp( argv[i], "/type:", 6 ) == 0 )
{
ServiceTypeName = argv[i] + 6;
}
else if ( _strnicmp( argv[i], "/size:", 6 ) == 0 )
{
TransferSize = atoi( argv[i] + 6 );
}
else if ( _strnicmp( argv[i], "/count:", 7 ) == 0 )
{
TransferCount = atoi( argv[i] + 7 );
}
else if ( _strnicmp( argv[i], "/rcvbuf:", 8 ) == 0 )
{
ReceiveBufferSize = atoi( argv[i] + 8 );
}
else if ( _strnicmp( argv[i], "/sndbuf:", 8 ) == 0 )
{
SendBufferSize = atoi( argv[i] + 8 );
}
else
{
printf( "Usage: rnrclnt [/name:SVCNAME] [/type:TYPENAME] [/size:N]\n" );
printf( " [/count:N] [/rcvbuf:N] [/sndbuf:N]\n" );
exit( 0 );
}
}

//
// Allocate memory to hold the network I/O buffer.
//

IoBuffer = malloc( TransferSize + 1 );
if ( IoBuffer == NULL )
{
printf( "Failed to allocate I/O buffer.\n" );
exit( 0 );
}

//
// Determine the type (GUID) of the service we are interested in
// connecting to.
//

err = GetTypeByName( ServiceTypeName, &serviceType );
if ( err == SOCKET_ERROR )
{
printf( "GetTypeByName for \"%s\" failed: %ld\n",
ServiceTypeName, GetLastError( ) );
exit( 0 );
}

//
// Open a connected socket to the service.
//

s = OpenConnection(
RemoteName,
&serviceType,
TRUE,
FALSE,
FALSE,
FALSE,
&protocol[0]
);

if ( s == INVALID_SOCKET )
{
printf( "Failed to open connection to name \"%s\" type \"%s\"\n",
RemoteName, ServiceTypeName );
exit( 0 );
}

//
// The connection succeeded. Display some information on the
// protocol which was used.
//

bytesRequired = sizeof(buffer);
protocol[1] = 0;

err = EnumProtocols( protocol, buffer, &bytesRequired );

if ( err < 1 )
{
printf( "EnumProtocols failed for protocol %ld: %ld\n",
protocol[0], GetLastError( ) );
exit( 0 );
}

err = GetNameByType( &serviceType, buffer2, sizeof(buffer2) );

if ( err != NO_ERROR )
{
printf( "GetNameByType failed: %ld\n", GetLastError( ) );
exit ( 0 );
}

protocolInfo = (PPROTOCOL_INFO)buffer;
printf( "Connected to %s/%s with protocol \"%s\" (%ld)\n",
RemoteName, buffer2,
protocolInfo->lpProtocol,
protocolInfo->iProtocol );

//
// Send data to and from the service.
//

DoEcho( s );

} // main


VOID
DoEcho(
IN SOCKET s )
{
INT err;
INT bytesReceived;
DWORD i;
DWORD startTime;
DWORD endTime;
DWORD transferStartTime;
DWORD transferEndTime;
DWORD totalTime;
INT thisTransferSize;
DWORD bytesTransferred = 0;

startTime = GetTickCount( );

for ( i = 0; i < TransferCount; i++ )
{
thisTransferSize = TransferSize;

transferStartTime = GetTickCount( );

err = send( s, IoBuffer, thisTransferSize, 0 );

if ( err != thisTransferSize )
{
printf( "send didn't work, ret = %ld, error = %ld\n",
err, GetLastError( ) );
closesocket( s );
return;
}

bytesReceived = 0;
do {
err = recv( s, IoBuffer, thisTransferSize, 0 );

if ( err == SOCKET_ERROR )
{
printf( "recv failed: %ld\n", GetLastError( ) );
closesocket( s );
return;
}
else if ( err == 0 && thisTransferSize != 0 )
{
printf( "socket closed prematurely by remote.\n" );
return;
}

bytesReceived += err;
} while ( bytesReceived < thisTransferSize );

transferEndTime = GetTickCount( );
printf( "%5ld bytes sent and received in %ld ms\n",
thisTransferSize, transferEndTime - transferStartTime );

bytesTransferred += thisTransferSize;
}

endTime = GetTickCount( );
totalTime = endTime - startTime;

printf( "\n%ld bytes transferred in %ld iterations, time = %ld ms\n",
bytesTransferred, TransferCount, totalTime );
printf( "Rate = %ld KB/s, %ld T/S, %ld ms/iteration\n",
(bytesTransferred / totalTime) * 2,
(TransferCount*1000) / totalTime,
totalTime / TransferCount );

err = closesocket( s );

if ( err == SOCKET_ERROR )
{
printf( "closesocket failed: %ld\n", GetLastError( ) );
return;
}

return;

} // DoEcho


SOCKET
OpenConnection(
IN PTSTR ServiceName,
IN LPGUID ServiceType,
IN BOOL Reliable,
IN BOOL MessageOriented,
IN BOOL StreamOriented,
IN BOOL Connectionless,
OUT PINT ProtocolUsed )

/*++

Routine Description:

Examines the Windows Sockets transport protocols loaded on a machine
and determines those which support the characteristics requested by
the caller. Attempts to locate and connect to the specified service
on these protocols.

Arguments:

ServiceName - a friendly name which identifies the service we want
to connect to. On name spaces which support name resolution at
the service level (e.g. SAP) this is the name clients will use
to connect to this service. On name spaces which support name
resolution at the host level (e.g. DNS) this name is ignored
and applications must use the host name to establish
communication with this service.

ServiceType - the GUID value which uniquely identifies the type of
service we provide. A GUID is created with the UUIDGEN program.

Reliable - if TRUE, the caller requests that only transport protocols
which support reliable data delivery be used. If FALSE, both
reliable and unreliable protocols may be used.

MessageOriented - if TRUE, only message-oriented transport protocols
should be used. If FALSE, the caller either does not care
whether the protocols used are message oriented or desires only
stream-oriented protocols.

StreamOriented - if TRUE, only stream-oriented transport protocols
should be used. If FALSE, the caller either does not care
whether the protocols used are stream oriented or desires only
message-oriented protocols.

Connectionless - if TRUE, only connectionless protocols should be
used. If FALSE, both connection-oriented and connectionless
protocols may be used.

ProtocolUsed - if a connection is opened successfully, this
parameter receives the protocol ID of the protocol used to
establish the connection.

Return Value:

A connected socket handle, or INVALID_SOCKET if the connection
could not be established.

--*/

{
INT protocols[MAX_PROTOCOLS+1];
AFPROTOCOLS afProtocols[MAX_PROTOCOLS+1];
BYTE buffer[2048];
DWORD bytesRequired;
INT err;
PPROTOCOL_INFO protocolInfo;
PCSADDR_INFO csaddrInfo = NULL;
INT protocolCount;
INT addressCount;
INT i;
DWORD protocolIndex;
SOCKET s;

//
// First look up the protocols installed on this machine. The
// EnumProtocols() API returns about all the Windows Sockets
// protocols loaded on this machine, and we'll use this information
// to identify the protocols which provide the necessary semantics.
//

bytesRequired = sizeof(buffer);

err = EnumProtocols( NULL, buffer, &bytesRequired );

if ( err <= 0 )
{
return INVALID_SOCKET;
}

//
// Walk through the available protocols and pick out the ones which
// support the desired characteristics.
//

protocolCount = err;
protocolInfo = (PPROTOCOL_INFO)buffer;

for ( i = 0, protocolIndex = 0;
i < protocolCount && protocolIndex < MAX_PROTOCOLS;
i++, protocolInfo++ )
{
//
// If "reliable" support is requested, then check if supported
// by this protocol. Reliable support means that the protocol
// guarantees delivery of data in the order in which it is sent.
// Note that we assume here that if the caller requested reliable
// service then they do not want a connectionless protocol.
//

if ( Reliable )
{
//
// Check to see if the protocol is reliable. It must
// guarantee both delivery of all data and the order in
// which the data arrives. Also, it must not be a
// connectionless protocol.
//

if ( (protocolInfo->dwServiceFlags &
XP_GUARANTEED_DELIVERY) == 0 ||
(protocolInfo->dwServiceFlags &
XP_GUARANTEED_ORDER) == 0 )
{
continue;
}

if ( (protocolInfo->dwServiceFlags & XP_CONNECTIONLESS) != 0 )
{
continue;
}

//
// Check to see that the protocol matches the stream/message
// characteristics requested. A stream oriented protocol
// either has the XP_MESSAGE_ORIENTED bit turned off, or
// else supports "pseudo stream" capability. Pseudo stream
// means that although the underlying protocol is message
// oriented, the application may open a socket of type
// SOCK_STREAM and the protocol will hide message boundaries
// from the application.
//

if ( StreamOriented &&
(protocolInfo->dwServiceFlags & XP_MESSAGE_ORIENTED) != 0 &&
(protocolInfo->dwServiceFlags & XP_PSEUDO_STREAM) == 0 )
{
continue;
}

if ( MessageOriented &&
(protocolInfo->dwServiceFlags & XP_MESSAGE_ORIENTED) == 0 )
{
continue;
}
}
else if ( Connectionless )
{
//
// Make sure that this is a connectionless protocol. In a
// connectionless protocol, data is sent as discrete
// datagrams with no connection establishment required.
// Connectionless protocols typically have no reliability
// guarantees.
//

if ( (protocolInfo->dwServiceFlags & XP_CONNECTIONLESS) != 0 )
{
continue;
}
}

//
// This protocol fits all the criteria. Add it to the list of
// protocols in which we're interested.
//
afProtocols[protocolIndex].iProtocol = protocolInfo->iProtocol;
afProtocols[protocolIndex].iAddressFamily = AF_UNSPEC;

protocols[protocolIndex++] = protocolInfo->iProtocol;
}

//
// Make sure that we found at least one acceptable protocol. If
// there no protocols on this machine which meet the caller's
// requirements then fail here.
//

if ( protocolIndex == 0 )
{
return INVALID_SOCKET;
}

afProtocols[protocolIndex].iProtocol = 0;
afProtocols[protocolIndex].iAddressFamily = 0;

protocols[protocolIndex] = 0;

//
// Now attempt to find the address of the service to which we're
// connecting. Note that we restrict the scope of the search to
// those protocols of interest by passing the protocol array we
// generated above to RnrGetAddressFromName() or GetAddressByName()
// depending on whether we are running the client on the same machine
// as the server rnrsrv.exe is running on. This forces
// RnrGetAddressFromName() or GetAddressByName() to return socket
// addresses for only the protocols we specify, ignoring possible
// addresses for protocols we cannot support because of the caller's
// constraints.
//

bytesRequired = sizeof( buffer );

if ( !strcmp( ServiceName, "localhost" ) )
{
//
// This is a Winsock 1.0 call . . .
//
err = GetAddressByName( NS_DEFAULT,
ServiceType,
ServiceName,
protocols,
0,
NULL,
buffer,
&bytesRequired,
NULL,
NULL );
}
else
{
//
// This calls into Winsock 2.0 . . .
//
err = Rnr20_GetAddressByName( ServiceName,
ServiceType,
NS_ALL,
protocolIndex,
afProtocols,
buffer,
&bytesRequired );
}

if ( err <= 0 )
{
return INVALID_SOCKET;
}

addressCount = err;
csaddrInfo = (PCSADDR_INFO) buffer;

//
// For each address, open a socket and attempt to connect. Note that
// if anything fails for a particular protocol we just skip on to
// the next protocol. As soon as we have established a connection,
// quit trying.
//

for ( i = 0; i < addressCount; i++, csaddrInfo++ )
{
//
// Open the socket. Note that we manually specify stream type
// if so requested in case the protocol is natively a message
// protocol but supports stream semantics.
//

s = socket( csaddrInfo->LocalAddr.lpSockaddr->sa_family,
StreamOriented ? SOCK_STREAM : csaddrInfo->iSocketType,
csaddrInfo->iProtocol );

if ( s == INVALID_SOCKET )
{
continue;
}

//
// Bind the socket to the local address specified.
//

err = bind( s, csaddrInfo->LocalAddr.lpSockaddr,
csaddrInfo->LocalAddr.iSockaddrLength );

if ( err != NO_ERROR )
{
closesocket( s );
continue;
}

//
// Attempt to connect the socket to the service. If this fails,
// keep trying on other protocols.
//

err = connect( s, csaddrInfo->RemoteAddr.lpSockaddr,
csaddrInfo->RemoteAddr.iSockaddrLength );

if ( err != NO_ERROR )
{
closesocket( s );
continue;
}

//
// The socket was successfully connected. Remember the protocol
// used and return the socket handle to the caller.
//

*ProtocolUsed = csaddrInfo->iProtocol;
return s;
}

if ( csaddrInfo )
{
(void) LocalFree( (HLOCAL) csaddrInfo );
}

//
// We failed to connect to the service.
//

return INVALID_SOCKET;

} // OpenConnection


INT
Rnr20_GetAddressByName(
IN PTSTR szServiceName,
IN LPGUID lpServiceType,
IN DWORD dwNameSpace,
IN DWORD dwNumberOfProtocols,
IN LPAFPROTOCOLS lpAfpProtocols,
IN OUT LPVOID lpCSAddrInfos,
IN OUT LPDWORD lpdwBufferLength )

/*++

Routine Description:

Calls Winsock 2.0 service lookup routines to find service addresses.

Arguments:

szServiceName - a friendly name which identifies the service we want
to find the address of.

lpServiceType - a GUID that identifies the type of service we want
to find the address of.

dwNameSpace - The Winsock2 Name Space to get address from (i.e. NS_ALL)

dwNumberOfProtocols - Size of the protocol constraint array, may be zero.

lpAftProtocols - (Optional) References an array of AFPROTOCOLS structure.
Only services that utilize these protocols will be returned.

lpCSAddrInfos - On successful return, this will point to an array of
CSADDR_INFO structures that contains the host address(es). Memory
is passed in by callee and the length of the buffer is provided by
lpdwBufferLength.

lpdwBufferLength - On input provides the length in bytes of the buffer
lpCSAddrInfos. On output returns the length of the buffer used or
what length the buffer needs to be to store the address.

Return Value:

The number of CSADDR_INFO structures returned in lpCSAddrInfos, or
(INVALID_SOCKET) with a WIN32 error in GetLastError.

--*/

{
ULONG dwLength = 2048; // Guess at buffer size
PWSAQUERYSETA pwsaQuerySet;
ULONG err;
HANDLE hRnR;
DWORD tempSize;
DWORD entries = 0;
DWORD dwNumberOfCsAddrs;

RtlZeroMemory( lpCSAddrInfos, *lpdwBufferLength );

pwsaQuerySet = (PWSAQUERYSETA) LocalAlloc( LMEM_ZEROINIT, dwLength );

if ( pwsaQuerySet == NULL )
{
//
// Unsuccessful.
//
return ERROR_NOT_ENOUGH_MEMORY;
}

RtlZeroMemory( pwsaQuerySet, dwLength );

//
// Do a lookup using RNRr.
//
pwsaQuerySet->dwSize = sizeof( WSAQUERYSETA );
pwsaQuerySet->lpszServiceInstanceName = szServiceName;
pwsaQuerySet->lpServiceClassId = lpServiceType;
pwsaQuerySet->lpVersion = 0;
pwsaQuerySet->lpszComment = 0;
pwsaQuerySet->dwNameSpace = dwNameSpace;
pwsaQuerySet->lpNSProviderId = 0;
pwsaQuerySet->lpszContext = 0;
pwsaQuerySet->dwNumberOfProtocols = dwNumberOfProtocols;
pwsaQuerySet->lpafpProtocols = lpAfpProtocols;

err = WSALookupServiceBegin( pwsaQuerySet,
LUP_RETURN_NAME |
LUP_RETURN_ADDR,
&hRnR );

if ( err != NO_ERROR )
{
err = WSAGetLastError();

//
// Free memory before returning.
//
(void) LocalFree( (HLOCAL) pwsaQuerySet );

//
// Unsuccessful.
//
return (DWORD) err;
}

//
// The query was accepted, so execute it via the Next call.
//
tempSize = dwLength;

err = WSALookupServiceNext( hRnR,
0,
&tempSize,
pwsaQuerySet );

if ( err != NO_ERROR )
{
err = WSAGetLastError();

if ( err == WSA_E_NO_MORE )
{
err = 0;
}

if ( err == WSASERVICE_NOT_FOUND )
{
err = WSAHOST_NOT_FOUND;
}

(void) LocalFree( (HLOCAL) pwsaQuerySet );

//
// Unsuccessful.
//
return (DWORD) err;

}

dwNumberOfCsAddrs = pwsaQuerySet->dwNumberOfCsAddrs;

if ( dwNumberOfCsAddrs > 0 )
{
//
// Make a copy of the CSAddrInfos returned from WSALookupServiceNext()
//
DWORD dwCSAddrInfoLen = dwNumberOfCsAddrs * sizeof( CSADDR_INFO );

if ( *lpdwBufferLength > dwCSAddrInfoLen )
{
RtlCopyMemory( lpCSAddrInfos,
pwsaQuerySet->lpcsaBuffer,
dwCSAddrInfoLen );
}
else
{
*lpdwBufferLength = dwCSAddrInfoLen;
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
dwNumberOfCsAddrs = INVALID_SOCKET;
}
}

//
// Close lookup service handle.
//
(VOID) WSALookupServiceEnd( hRnR );

//
// Free memory used for query set info.
//
(void) LocalFree( (HLOCAL) pwsaQuerySet );

return dwNumberOfCsAddrs;

} // RnrGetHostFromName