2025-11-28 00:35:46 +09:00

1371 lines
50 KiB
C++

//----------------------------------------------------------------------------
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1998 - 2000 Microsoft Corporation. All Rights Reserved.
//
// Module:
// RnrCs.cpp
//
// Abstract:
// In a traditional Winsock 1.1 client/server program, the server
// binds itself to a well known port of a particular protocol family.
// Then, any client can talk to the server only if it knows the server's
// addressing infomation which includes the server's address, protocol,
// and socket type.
//
// The Winsock 2 Name Resolution and Registration (RnR) provides a unified
// API set for client to search for registered services without having any
// prior knowledge of addressing information. Basically, a server wants to
// register its existence within one or more name spaces through a friendly
// service instance name and associate itself to a service class GUID.
// Client application will be able to find the addressing
// information of a server by the friendly name and class GUID
// in a name space.
//
// In this sample, the server program converts the user supplied Type Id
// into a service class GUID by SVCID_NETWARE() macro. (You can also
// generate your own service class GUID using uuidgen.exe or guidgen.exe
// and distribute this GUID to your client program.) It then checks the
// availability of various kind of name space providers and install this
// service class by filling out the relevant service class information
// with NS_NTDS (NT Directory Service name space provider available in Windows 2000)
// and/or NS_SAP (Service Advertising Protocol name space provider available
// both in Windows NT 4 and Windows 2000) name spaces whenever they are available.
// If NS_NTDS has been installed, it binds itself to UDP protocol addresses
// and a port number dynamically chosen by the system. If NS_SAP has been
// installed, it also binds itself to a IPX protocol address and a socket
// number chosen by the system. After filling out its bound socket addresses,
// it advertises its availability through a service name supplied by a user.
// For each bound non-blocking socket addresses, the server tries to
// receive any datagram sent by any client and print out the client's socket
// addressing information if a datagram is received.
// When a user hits CTRL-C, the server program will unregister this
// service instance, remove the service class and close all sockets.
//
// The client program converts the user supplied Type Id into a service
// class GUID by SVCID_NETWARE() macro, such that it can find any SAP
// predefined services (e.g. file server Type Id is 4) or the corresponding
// advertised server program. If the user supplies a service name, it
// will just look up the service with the same service name and Type Id.
// If the user supplies a wild card "*" for the service name, it will try
// to look up all registered services with the same
// Type Id. For each service program found, it prints out the socket addressing
// information of the service program and sends a message to it.
//
// NOTE:
// (1) To exploit the NS_NTDS name space (Active Directory):
// (a) client and server machines should have Windows 2000 or later installed and both
// machines should be members of a Windows 2000 domain.
// (b) If the server program is running in the security context of Domain Admins,
// you should have no problem in publishing the service.
// (c) If the server program is running in the context of a Domain Users, Domain
// Admins have to modify the access control list (ACLs) on the WinsockServices
// container:
// From the Active Directory Users and Computers MMC snap-in, access the View menu
// and select Advanced Features. Under the System container is another container
// listed as WinsockServices. Open the WinsockServices container,
// right-click Properties->Security, add your desired user/group with Full Control
// permission. Make sure this applies onto "this object and all child objects"
// by clicking the "Advanced..." button and "View/Edit..." your selected permission
// entry.
// (2) To advertise in the SAP name space, server program machine should have
// NWLink IPX/SPX Compatible Transport protocol and SAP Agent service installed.
// (3) To lookup servers in the SAP name space, client program machine should have
// NWLink IPX/SPX Compatible Transport protocol installed.
// (4) When the server is running on a mutihomed machine, the SAP name space
// provider will only make the first registered ipx address available, other
// ipx addresses are ignored.
//
// Usage: RnrCs
// -c (running this program as a client)
// -s (running this program as a server)
// -t server_type_id (default is: 200)
// -n server_instance_name (default is: MyServer)
// -p provider_name (default is: NS_ALL)
// (e.g. NS_NTDS, NS_SAP, NS_ALL)
// Examples:
// RnrCs -s -n MyServer ....Run as server /w server_type_id of 200, service
// instance name of MyServer
// RnrCs -c -n * .............Run as client and find all servers /w server_type_id
// of 200 from all available name spaces
// RnrCs -c -n * -p NS_SAP ...Run as client and find all servers /w server_type_id
// of 200 from the SAP name space
// RnrCs -c -t 4 -n * ........Run as client and find all SAP file servers
// Hit Ctrl-C to quit the server program
//
// Build:
// Use the headers and libs from the April98 Platform SDK or later.
// Link with ws2_32.lib
//
// Author: Frank K. Li - Microsoft Developer Support
//
//----------------------------------------------------------------------------
#ifdef _IA64_
#pragma warning (disable: 4127 4267)
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#include <wspiapi.h>
#include <svcguid.h>
#include <wsipx.h>
#include <wsnwlink.h>
#include <nspapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <strsafe.h>
#define DEFAULT_STRING_LEN 256
void GetSockAddrString(SOCKADDR_STORAGE* pSAddr, int addrlen, char * dest, int destlen);
void SetIpPort(SOCKADDR_STORAGE *dest, SOCKADDR_STORAGE *src);
void DumpServiceClassInfo(LPWSASERVICECLASSINFO lpSci);
//-------Server side structures and functions-------------------
// Global socket handles
const int g_nMaxNumOfCSAddr = 20; // advertise at most 10 addresses
const int g_nMaxNumOfSocks = 3; // one for udp/ipv4, one for udp/ipv6, another for ipx
int g_nNumOfUsedSocks = 0; // number of socket created
SOCKET g_aSock[g_nMaxNumOfSocks] = {INVALID_SOCKET, INVALID_SOCKET, INVALID_SOCKET};
CSADDR_INFO g_aCSAddr[g_nMaxNumOfCSAddr] = {{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}};
SOCKADDR_STORAGE g_aSockAddr[g_nMaxNumOfCSAddr] = {{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}}; // SOCKADDR buffer
GUID g_MyGuid; // guid for this service class
WSAQUERYSET g_QS = {0}; // QuerySet to advertise service
SOCKADDR_STORAGE ss_in = {0}; // socket address for AF_INET
SOCKADDR_IPX sa_ipx = {0}; // socket address for AF_IPX
BOOL g_fSap = FALSE; // available name space providers on local machine
BOOL g_fNtds = FALSE;
DWORD g_dwNumNs = 0; // total number of name space providers
BOOL fEndProgram = FALSE; // signal the end of program when user hits "Ctrl-C"
BOOL DoRnrServer(char * pszServerName, int nServerType);
BOOL InstallClass(int nServerType);
BOOL Advertise(char* pszServerName);
BOOL ServerRecv();
BOOL CheckAvailableNameSpaceProviders(BOOL& fNsSap, BOOL& fNsNtds);
BOOL GetBoundIpxAddr(SOCKET soc, SOCKADDR_IPX * pSaIpx, int nAdapter);
BOOL WINAPI CtrlHandler ( DWORD dwEvent );
//-------Client side functions------------------------------
void DoRnrClient (int nServiceType, char * pszServerName, DWORD dwNameSpace);
void ClientSend(CSADDR_INFO* lpcsaBuffer);
void PrintError (char* errfunc);
#define OFFSET 1024 // a large buffer to hold returned WSAQUERYSET
//--------------------------------------------------------------
void Usage(char * pszProgramName)
{
fprintf(stderr, "Usage: %s\n", pszProgramName);
fprintf(stderr, " -c (running this program as a client)\n");
fprintf(stderr, " -s (running this program as a server)\n");
fprintf(stderr, " -t server_type_id (default is: 200)\n");
fprintf(stderr, " -n server_instance_name (default is: MyServer) \n");
fprintf(stderr, " -p provider name (default is: NS_ALL) \n");
fprintf(stderr, " (e.g. NS_NTDS, NS_SAP, NS_ALL)\n");
fprintf(stderr, "Examples:\n");
fprintf(stderr, "RnrCs -s -n MyServer ...Run as server /w server_type_id of 200, service\n");
fprintf(stderr, " instance name of MyServer\n");
fprintf(stderr, "RnrCs -c -n * ............Run as client and find all servers /w server_type_id\n");
fprintf(stderr, " of 200 from all available name spaces\n");
fprintf(stderr, "RnrCs -c -n * -p NS_SAP ..Run as client and find all servers /w server_type_id\n");
fprintf(stderr, " of 200 from the SAP name space\n");
fprintf(stderr, "RnrCs -c -t 4 -n * .......Run as client and find all SAP file servers\n");
fprintf(stderr, "Hit Ctrl-C to quit the server program\n");
}
void __cdecl main (int argc, char *argv[])
{
char * pszServerName = "MyServer"; // default server instance name
int nServerType = 200; // default server type id
char * pszNSProvider = "NS_ALL";
DWORD dwNameSpace = NS_ALL;
int i = 0;
BOOL fServer = TRUE; // by default, running this program as the server
// allow the user to override settings with command line switches
for (i = 1; i < argc; i++)
{
if ((*argv[i] == '-') || (*argv[i] == '/'))
{
switch (tolower(*(argv[i]+1)))
{
case 'n': // server instance name
pszServerName = argv[++i];
break;
case 't': // server_type, this will be used as the base for the Server class id
nServerType = atoi(argv[++i]);
break;
case 'c':
fServer = FALSE;
break;
case 's':
fServer = TRUE;
break;
case 'p':
pszNSProvider = argv[++i];
if (_strnicmp("NS_NTDS", pszNSProvider, strlen("NS_NTDS")) == 0)
dwNameSpace = NS_NTDS;
else if (_strnicmp("NS_SAP", pszNSProvider, strlen("NS_SAP")) == 0)
dwNameSpace = NS_SAP;
// default is NS_ALL
break;
case 'h':
case '?':
default:
Usage(argv[0]);
return;
}
}
else
{
Usage(argv[0]);
return;
}
}
if (fServer)
{
// check user input
if (strcmp(pszServerName, "*") == 0)
{
printf("You can't supply wildcard (*) server instance name to run this server\n");
Usage(argv[0]);
return;
}
}
__try
{
// init winsock
WSADATA wd;
int nRet;
if ( (nRet = WSAStartup (MAKEWORD (2, 2), &wd)) != 0)
{
printf ("Unable to start Winsock 2. Error: %d\n", nRet);
__leave;
}
if (fServer)
{
// running as a server program
DoRnrServer(pszServerName, nServerType);
}
else
{
// running as a client program
DoRnrClient(nServerType, pszServerName, dwNameSpace);
}
}
__finally
{
if (fServer)
{
SetConsoleCtrlHandler(CtrlHandler, FALSE);
}
WSACleanup();
}
}
//---------------------------------------------
// server side routines
//---------------------------------------------
//---------------------------------------------------------------------------
// FUNCTION: BOOL DoRnrServer(char * pszServerName, int nServerType)
//
// PURPOSE: The driver for the server side code. It forms the global
// class id "g_MyGuid" for this service class, installs this
// service class, advertises the service name "pszServerName" with
// the associated bound SOCKADDR addresses and then loop through
// for data sent by client. This routine returns if user hits "Ctrl-C"
// or any error encountered.
//
// RETURNS:
// TRUE if user hits "Ctrl-C" to end the server program.
// FALSE if there is any error encountered.
//
//---------------------------------------------------------------------------
BOOL DoRnrServer(char * pszServerName, int nServerType)
{
// I am using SVCID_NETWARE macro to form a common class id for the
// client and server program such that the client program can also query
// some known SAP services on the network without running this program
// as a server.
// You can also generate your own class id by uuidgen.exe or guidgen.exe and
// distribute the class id to client program.
//
// Please check svcguid.h for details about the macro.
GUID guidNW = SVCID_NETWARE(nServerType);
memcpy(&g_MyGuid, &guidNW, sizeof(GUID));
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE))
{
printf ("SetConsoleCtrlHandler failed to install console handler: %d\n",
GetLastError());
return FALSE;
}
// Install the server class
if (!InstallClass(nServerType))
{
return FALSE;
}
// advertise the server instance
if (!Advertise(pszServerName))
{
return FALSE;
}
// Make our bound sockets non-blocking such that
// we can loop and test for data sent by client
// without blocking.
u_long arg = 1L;
for (int i = 0; i < g_nNumOfUsedSocks; i++)
{
// make the socket as non-blocking socket
if (ioctlsocket(g_aSock[i], FIONBIO, &arg) == SOCKET_ERROR)
{
printf("ioctlsocket[%d] error %d\n", i, WSAGetLastError());
return FALSE;
}
}
// receive data from client who find our address thru Winsock 2 RnR
for (;;)
{
if (ServerRecv() == FALSE)
{
return FALSE;
}
if (fEndProgram)
return TRUE;
Sleep(100);
}
}
//---------------------------------------------------------------------------
// FUNCTION: InstallClass(int nServerType)
//
// PURPOSE: Install a service class in NTDS and SAP name spaces if they are
// available on this machine. "nServerType" is a numerical number used
// to generate a service class id by using SVCID_NETWARE macro as shown in
// the main program.
// The name of this service class is obtained by composing "nServerType"
// with the string "TypeId.
//
// RETURNS:
// TRUE if succeed otherwise FALSE
//
//---------------------------------------------------------------------------
BOOL InstallClass(int nServerType)
{
WSASERVICECLASSINFO sci;
WSANSCLASSINFO aNameSpaceClassInfo[4] = {{0},{0},{0},{0}};
DWORD dwSapId = nServerType;
DWORD dwZero = 0;
TCHAR szServiceClassName[DEFAULT_STRING_LEN] = {'\0'};
int nRet = 0;
DWORD dwUdpPort = 0; // we use dynamic generated port number
HRESULT hRet;
if (FAILED(hRet = StringCchPrintf(szServiceClassName,
sizeof(szServiceClassName)/sizeof(szServiceClassName[0]),
"TypeId %d",
nServerType
)))
{
printf("StringCchPrintf failed: 0x%x\n",hRet);
return FALSE;
}
printf("Installing ServiceClassName: %s\n", szServiceClassName);
BOOL fSap = FALSE;
BOOL fNtds = FALSE;
// see what name space providers are available
if (!CheckAvailableNameSpaceProviders(fSap, fNtds))
return FALSE;
// save a copy
g_fSap = fSap;
g_fNtds = fNtds;
// our total number of interested name space providers
if (fSap)
g_dwNumNs++;
if (fNtds)
g_dwNumNs++;
// setup Service Class info
SecureZeroMemory(&sci,sizeof(sci));
sci.lpServiceClassId = (LPGUID) &g_MyGuid;
sci.lpszServiceClassName = (LPSTR) szServiceClassName;
sci.dwCount = g_dwNumNs * 2; // each name space has 2 NameSpaceClassInfo
sci.lpClassInfos = aNameSpaceClassInfo;
SecureZeroMemory(aNameSpaceClassInfo,sizeof(WSANSCLASSINFO)*4);
DWORD i =0; // index to array of WSANSCLASSINFO
// common service class registration in NTDS and SAP name spaces
if (fNtds)
{
// NTDS setup
printf("NTDS name space class installation\n");
aNameSpaceClassInfo[i].lpszName = SERVICE_TYPE_VALUE_CONN;
aNameSpaceClassInfo[i].dwNameSpace = NS_NTDS;
aNameSpaceClassInfo[i].dwValueType = REG_DWORD;
aNameSpaceClassInfo[i].dwValueSize = sizeof(DWORD);
aNameSpaceClassInfo[i].lpValue = &dwZero;
i++; // increment of the index
aNameSpaceClassInfo[i].lpszName = SERVICE_TYPE_VALUE_UDPPORT;
aNameSpaceClassInfo[i].dwNameSpace = NS_NTDS;
aNameSpaceClassInfo[i].dwValueType = REG_DWORD;
aNameSpaceClassInfo[i].dwValueSize = sizeof(DWORD);
aNameSpaceClassInfo[i].lpValue = &dwUdpPort;
i++; // increment of the index
}
if (fSap)
{
// SAP setup
printf("SAP name space class installation\n");
aNameSpaceClassInfo[i].lpszName = SERVICE_TYPE_VALUE_CONN;
aNameSpaceClassInfo[i].dwNameSpace = NS_SAP;
aNameSpaceClassInfo[i].dwValueType = REG_DWORD;
aNameSpaceClassInfo[i].dwValueSize = sizeof(DWORD);
aNameSpaceClassInfo[i].lpValue = &dwZero;
i++; // increment of the index
aNameSpaceClassInfo[i].lpszName = SERVICE_TYPE_VALUE_SAPID;
aNameSpaceClassInfo[i].dwNameSpace = NS_SAP;
aNameSpaceClassInfo[i].dwValueType = REG_DWORD;
aNameSpaceClassInfo[i].dwValueSize = sizeof(DWORD);
aNameSpaceClassInfo[i].lpValue = &dwSapId;
}
// Install the service class information
DumpServiceClassInfo(&sci);
nRet = WSAInstallServiceClass(&sci);
if (nRet == SOCKET_ERROR)
{
printf("WSAInstallServiceClass error %d\n", WSAGetLastError());
return FALSE;
}
return TRUE;
}
//---------------------------------------------------------------------------
// FUNCTION: Advertise(char* pszServerName)
//
// PURPOSE: Given the name of this service instance "pszServerName",
// advertise this instance to the available name spaces.
//
// RETURNS:
// TRUE if succeed otherwise FALSE
//
//---------------------------------------------------------------------------
BOOL Advertise(char* pszServerName)
{
int nRet = 0;
int i=0; // number of socket created
int nNumOfCSAddr = 0; // number of bound socket addresses
// Advertizing
// Set up the WSAQuery data
SecureZeroMemory(&g_QS,sizeof(WSAQUERYSET));
g_QS.dwSize = sizeof(WSAQUERYSET);
g_QS.lpszServiceInstanceName = (LPSTR)pszServerName; // service instance name
g_QS.lpServiceClassId = &g_MyGuid; // associated service class id
g_QS.dwNameSpace = NS_ALL; // advertise to all name spaces
g_QS.lpNSProviderId = NULL;
g_QS.lpcsaBuffer = g_aCSAddr; // our bound socket addresses
g_QS.lpBlob = NULL;
// Set up the g_aCSAddr data
if (g_fNtds)
{
SOCKET_ADDRESS_LIST *slist=NULL;
struct addrinfo *res=NULL,
*resptr=NULL,
hints;
char *addrbuf=NULL;
DWORD dwBytes=0;
SecureZeroMemory(&hints,sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
nRet = getaddrinfo(NULL, "0", &hints, &res);
if ((nRet != 0) || (res == NULL))
{
return FALSE;
}
resptr = res;
while (resptr)
{
g_aSock[i] = socket(resptr->ai_family, resptr->ai_socktype, resptr->ai_protocol);
if (g_aSock[i] == INVALID_SOCKET)
{
printf("socket error %d\n", WSAGetLastError());
freeaddrinfo(res);
return FALSE;
}
// bind to local host ip addresses and let system to assign a port number
nRet = bind ( g_aSock[i], resptr->ai_addr, (int)resptr->ai_addrlen);
if ( SOCKET_ERROR == nRet )
{
printf("bind error %d\n", WSAGetLastError());
freeaddrinfo(res);
return FALSE;
}
int cb = sizeof(ss_in);
if (getsockname(g_aSock[i], (SOCKADDR *) &ss_in, &cb) == SOCKET_ERROR)
{
printf("getsockname error %d\n", WSAGetLastError());
freeaddrinfo(res);
return FALSE;
}
nRet = WSAIoctl(g_aSock[i], SIO_ADDRESS_LIST_QUERY, NULL, 0, NULL, 0, &dwBytes, NULL, NULL);
if (nRet == SOCKET_ERROR)
{
addrbuf = (char *)HeapAlloc(GetProcessHeap(), 0, dwBytes);
if (addrbuf == NULL)
{
freeaddrinfo(res);
return FALSE;
}
nRet = WSAIoctl(g_aSock[i], SIO_ADDRESS_LIST_QUERY, NULL, 0, addrbuf, dwBytes, &dwBytes, NULL, NULL);
if (nRet == SOCKET_ERROR)
{
printf("WSAIoctl failed: %d\n", WSAGetLastError());
HeapFree(GetProcessHeap(), 0, addrbuf);
freeaddrinfo(res);
return FALSE;
}
}
else
{
printf("WSAIoctl should have failed!\n");
freeaddrinfo(res);
return FALSE;
}
slist = (SOCKET_ADDRESS_LIST *)addrbuf;
if (resptr->ai_family == AF_INET)
printf("IPv4 addresses bound...\n");
else if (resptr->ai_family == AF_INET6)
printf("IPv6 addresses bound...\n");
else
printf("Unknown address family addresses bound...\n");
for (int j = 0; j < slist->iAddressCount ; j++)
{
if (j >= g_nMaxNumOfCSAddr)
{
printf("Max. number of socket address (%d) reached. We will not advertise extra ones\n", g_nMaxNumOfCSAddr);
break;
}
// Copy the address over
memcpy(&g_aSockAddr[j], slist->Address[j].lpSockaddr, slist->Address[j].iSockaddrLength);
// Set the port number our socket is actually bound to
SetIpPort(&g_aSockAddr[j],&ss_in);
char temp[128] = {'\0'};
GetSockAddrString (&g_aSockAddr[j], slist->Address[j].iSockaddrLength, temp, 128);
printf("Address %40s\n", temp);
g_aCSAddr[j].iSocketType = SOCK_DGRAM;
g_aCSAddr[j].iProtocol = IPPROTO_UDP;
g_aCSAddr[j].LocalAddr.lpSockaddr = (struct sockaddr *)&g_aSockAddr[j];
g_aCSAddr[j].LocalAddr.iSockaddrLength = slist->Address[j].iSockaddrLength;
g_aCSAddr[j].RemoteAddr.lpSockaddr = (struct sockaddr *)&g_aSockAddr[j];
g_aCSAddr[j].RemoteAddr.iSockaddrLength = slist->Address[j].iSockaddrLength;
nNumOfCSAddr++; // increase the number SOCKADDR buffer used
}
i++; // increase the number of socket created
// Go to the next address
resptr = resptr->ai_next;
// Free the address buffer
HeapFree(GetProcessHeap(), 0, addrbuf);
addrbuf = NULL;
}
freeaddrinfo(res);
}
if (g_fSap && (nNumOfCSAddr < g_nMaxNumOfCSAddr))
{
// advertise into the NS_SAP name space if we still have enough
// socket address buffer
SecureZeroMemory(sa_ipx.sa_netnum,sizeof(sa_ipx.sa_netnum));
SecureZeroMemory(sa_ipx.sa_nodenum,sizeof(sa_ipx.sa_nodenum));
sa_ipx.sa_family = AF_IPX;
sa_ipx.sa_socket = 0;
g_aSock[i] = socket ( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );
if ( INVALID_SOCKET == g_aSock[i] )
{
printf("socket error %d\n", WSAGetLastError());
return FALSE;
}
nRet = bind ( g_aSock[i], (SOCKADDR *) &sa_ipx, sizeof (sa_ipx) );
if ( SOCKET_ERROR == nRet )
{
printf("bind error %d\n", WSAGetLastError());
return FALSE;
}
//--------------Find out our bound ipx addresses------------
int cb = 0; // size variable
int nAdapters = 0; // number of adapters bound with IPX
// get the number of adapters
cb = sizeof(nAdapters);
nRet = getsockopt(g_aSock[i], NSPROTO_IPX, IPX_MAX_ADAPTER_NUM, (char *) &nAdapters, &cb);
if (nRet == SOCKET_ERROR)
{
printf("getsockopt error %d\n", WSAGetLastError());
return FALSE;
}
SOCKADDR_IPX* pSaIpx = NULL; // a pointer to our current SOCKADDR buffer
printf("IPX addresses bound...\n");
for (int nIdx = 0; nIdx < nAdapters; nIdx++)
{
if (nNumOfCSAddr >= g_nMaxNumOfCSAddr)
{
printf("Max. number of socket address (%d) reached. We will not advertise extra ones\n", g_nMaxNumOfCSAddr);
break;
}
// get the buffer for this SOCKADDR
pSaIpx = (SOCKADDR_IPX *) &g_aSockAddr[nNumOfCSAddr];
if (GetBoundIpxAddr(g_aSock[i], pSaIpx, nIdx) == FALSE)
{
printf("No valid bound IPX address at adapter index %d\n", nIdx);
// since this link is not available, nNumOfCSAddr will reuse the current SOCKADDR buffer
continue;
}
char temp[DEFAULT_STRING_LEN] = {'\0'};
GetSockAddrString (&g_aSockAddr[nNumOfCSAddr], sizeof(SOCKADDR_IPX), temp, DEFAULT_STRING_LEN);
printf("%40s\n", temp);
g_aCSAddr[nNumOfCSAddr].iSocketType = SOCK_DGRAM;
g_aCSAddr[nNumOfCSAddr].iProtocol = NSPROTO_IPX;
g_aCSAddr[nNumOfCSAddr].LocalAddr.lpSockaddr = (struct sockaddr *)&g_aSockAddr[nNumOfCSAddr];
g_aCSAddr[nNumOfCSAddr].LocalAddr.iSockaddrLength = sizeof(sa_ipx);
g_aCSAddr[nNumOfCSAddr].RemoteAddr.lpSockaddr = (struct sockaddr *)&g_aSockAddr[nNumOfCSAddr];
g_aCSAddr[nNumOfCSAddr].RemoteAddr.iSockaddrLength = sizeof(sa_ipx);
nNumOfCSAddr++;
}
i++; // increase the number of socket created
// Note: If g_fNtds is TRUE, this ipx address will be
// available from the NTDS name space too.
}
// update counters
g_QS.dwNumberOfCsAddrs = nNumOfCSAddr;
g_nNumOfUsedSocks = i;
// Call WSASetService
printf("Advertise server of instance name: %s ...\n", pszServerName);
nRet = WSASetService(&g_QS, RNRSERVICE_REGISTER, 0L);
if (nRet == SOCKET_ERROR)
{
printf("WSASetService error %d\n", WSAGetLastError());
return FALSE;
}
printf("Wait for client talking to me, hit Ctrl-C to terminate...\n");
return TRUE;
}
//---------------------------------------------------------------------------
// FUNCTION: GetBoundIpxAddr(SOCKET soc, SOCKADDR_IPX * pSaIpx, int nAdapter)
//
// PURPOSE: Given a bound socket "soc", fill up "pSaIpx" with a valid
// ipx address corresponding to the 0-based adapter index "nAdapter".
//
// RETURNS:
// TRUE if succeed otherwise FALSE
//
//---------------------------------------------------------------------------
BOOL GetBoundIpxAddr(SOCKET soc, SOCKADDR_IPX * pSaIpx, int nAdapter)
{
IPX_ADDRESS_DATA ipx_data = {0}; // see wsnwlink.h for details
int cb = 0; // size variable
int nRet = 0;
cb = sizeof ( IPX_ADDRESS_DATA );
SecureZeroMemory(&ipx_data,sizeof(IPX_ADDRESS_DATA));
ipx_data.adapternum = nAdapter;
nRet = getsockopt ( soc, NSPROTO_IPX, IPX_ADDRESS, (char *) &ipx_data, &cb );
if ( SOCKET_ERROR == nRet)
{
printf("getsockopt error %d\n", WSAGetLastError());
return FALSE;
}
cb = sizeof(SOCKADDR_IPX);
if (getsockname(soc, (SOCKADDR *) pSaIpx, &cb) == SOCKET_ERROR)
{
printf("getsockname error %d\n", WSAGetLastError());
return FALSE;
}
else
{
if (ipx_data.status == TRUE)
{
// this link is UP
memcpy(pSaIpx->sa_netnum, ipx_data.netnum, sizeof(pSaIpx->sa_netnum));
memcpy(pSaIpx->sa_nodenum, ipx_data.nodenum, sizeof(pSaIpx->sa_nodenum));
return TRUE;
}
else
return FALSE; // this link is DOWN
}
}
//---------------------------------------------------------------------------
// FUNCTION: CheckAvailableNameSpaceProviders(BOOL& fNsSap, BOOL& fNsNtds)
//
// PURPOSE: Check the avaliable name space providers, set fNsSap to TRUE if
// SAP is available, and fNsNtds to TRUE if NT Directory Service
// is avaialble.
//
// RETURNS:
// TRUE if succeed otherwise FALSE
//
// NOTE: In this sample, we are only interested to know if NS_NTDS or NS_SAP
// name space provoder is available.
//---------------------------------------------------------------------------
BOOL CheckAvailableNameSpaceProviders(BOOL& fNsSap, BOOL& fNsNtds)
{
LPWSANAMESPACE_INFO pInfo = NULL;
DWORD dwBufLen = 0;
PBYTE pBuf = NULL;
int nCount = 0;
int nRet = 0;
dwBufLen = 0;
fNsSap = fNsNtds = FALSE;
nRet = WSAEnumNameSpaceProviders(&dwBufLen, NULL);
if (nRet == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAEFAULT)
{
printf("Error %d\n", WSAGetLastError());
return FALSE;
}
}
// dwBufLen contains the needed buffer size
pBuf = (PBYTE) HeapAlloc(GetProcessHeap(), 0, dwBufLen);
if (pBuf == NULL)
{
printf("\nCould not allocate buffer\n");
return FALSE;
}
nRet = WSAEnumNameSpaceProviders(&dwBufLen, (LPWSANAMESPACE_INFO)pBuf);
if (nRet == SOCKET_ERROR)
{
printf("Error: %d\n", WSAGetLastError());
HeapFree(GetProcessHeap(), 0, pBuf);
return FALSE;
}
//Loop thru the returned info
pInfo = (LPWSANAMESPACE_INFO)pBuf;
for (nCount = 0; nCount < nRet; nCount++)
{
switch (pInfo->dwNameSpace)
{
case NS_SAP:
fNsSap = TRUE;
break;
case NS_NTDS:
fNsNtds = TRUE;
break;
default:
break;
}
pInfo++;
}
HeapFree(GetProcessHeap(), 0, pBuf);
return TRUE;
}
//---------------------------------------------------------------------------
// FUNCTION: BOOL ServerRecv()
//
// PURPOSE: For each bound socket in g_aSock[], try to do a non-blocking
// receive and print out the peer's address information.
//
// RETURNS:
// TRUE if succeed otherwise FALSE
//---------------------------------------------------------------------------
BOOL ServerRecv()
{
int nBytesReceived = 0;
char szBuf[1024] = {'\0'};
SOCKADDR_STORAGE sa_peer = {0};
int nPeerAddrLen = 0;
for (int i = 0; i < g_nNumOfUsedSocks; i++)
{
nPeerAddrLen = sizeof(SOCKADDR_STORAGE);
// You can use "nBytesReceived = recv(g_aSock[i], szBuf, sizeof(szBuf), 0 );", if
// you don't want your peer's address information.
nBytesReceived = recvfrom(g_aSock[i], szBuf, sizeof(szBuf), 0, (SOCKADDR*)&sa_peer, &nPeerAddrLen);
if (nBytesReceived == SOCKET_ERROR)
{
int nRet = WSAGetLastError();
if (nRet == WSAEWOULDBLOCK || nRet == WSAEMSGSIZE)
continue;
else
{
printf("recv error: %d\n", nRet);
return FALSE;
}
}
else
{
printf("received: [%s ", szBuf);
char temp[DEFAULT_STRING_LEN] = {'\0'};
GetSockAddrString (&sa_peer, nPeerAddrLen, temp, DEFAULT_STRING_LEN);
printf(": %s]\n", temp);
}
}
return TRUE;
}
//---------------------------------------------------------------------------
// FUNCTION: BOOL WINAPI CtrlHandler ( DWORD dwEvent )
// Intercept CTRL-C or CTRL-BRK events and cause the server to
// initiate shutdown and cleanup.
//---------------------------------------------------------------------------
BOOL WINAPI CtrlHandler ( DWORD dwEvent )
{
int nRet = 0;
int i = 0;
WCHAR szGuid[MAX_PATH] = {'\0'};
HRESULT hRet;
switch (dwEvent)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
case CTRL_CLOSE_EVENT:
fEndProgram = TRUE;
printf("CtrlHandler: cleaning up...\n");
printf("delete service instance...\n");
nRet = WSASetService(&g_QS, RNRSERVICE_DELETE, 0L);
if (nRet == SOCKET_ERROR)
{
printf("WSASetService DELETE error %d\n", WSAGetLastError());
} else
printf(" Deleted.\n");
printf("Removing Service class ");
if (SUCCEEDED(hRet = StringCchPrintfW(szGuid,
sizeof(szGuid)/sizeof(szGuid[0]),
L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
g_MyGuid.Data1,
g_MyGuid.Data2,
g_MyGuid.Data3,
g_MyGuid.Data4[0],
g_MyGuid.Data4[1],
g_MyGuid.Data4[2],
g_MyGuid.Data4[3],
g_MyGuid.Data4[4],
g_MyGuid.Data4[5],
g_MyGuid.Data4[6],
g_MyGuid.Data4[7]
)))
{
wprintf(L"%s",szGuid);
}
printf("... \n");
nRet = WSARemoveServiceClass(&g_MyGuid);
if (nRet == SOCKET_ERROR)
{
printf("WSARemoveServiceClass error %d\n", WSAGetLastError());
} else
printf(" Removed.\n");
for (i = 0; i < g_nMaxNumOfSocks; i++)
{
if ( g_aSock[i] != INVALID_SOCKET )
{
closesocket ( g_aSock[i] );
g_aSock[i] = INVALID_SOCKET;
}
}
break;
default:
// unknown type--better pass it on.
return FALSE;
}
return TRUE;
}
//-------------------------------------
// Client side routines
//-------------------------------------
//---------------------------------------------------------------------------
// FUNCTION: void DoRnrClient (int nServiceType, char * pszServerName, DWORD dwNameSpace)
//
// PURPOSE: Given the service type id "nServiceType", the server instance name
// "pszServerName" and the name space to query "dwNameSpace",
// perform name resolution to the server and send a message to it.
// If "nServiceType" is some known SAP Id such as Print Queue (3),
// File Server (4), Job Server (5), Print Server (7), Archive
// Server (9), Remote Bridge Server (36) or Advertising Print Server
// (71), it will not send a message to the server.
//---------------------------------------------------------------------------
void DoRnrClient (int nServiceType, char * pszServerName, DWORD dwNameSpace)
{
static GUID guid = SVCID_NETWARE ( nServiceType ); //use this as the class id
WSAQUERYSET qs = {0};
AFPROTOCOLS afp[g_nMaxNumOfSocks] = {{AF_IPX, NSPROTO_IPX}, {AF_INET, IPPROTO_UDP}, {AF_INET6, IPPROTO_UDP}};
HANDLE hLookup = NULL;
DWORD dwResult = 0;
static char szName[100] = {'\0'};
BOOL fKnownSapId = FALSE;
DWORD dwLength = 0;
BYTE abyBuf[sizeof(WSAQUERYSET) + OFFSET] = {0}; // provide a sufficient large
// buffer for returned query set
WSAQUERYSET * pQS = (WSAQUERYSETA*) abyBuf;
HRESULT hRet;
if(FAILED(hRet = StringCchCopy(szName,
sizeof(szName)/sizeof(szName[0]),
pszServerName
)))
{
printf("StringCchCopy failed: 0x%x\n",hRet);
return;
}
SecureZeroMemory (&qs, sizeof (WSAQUERYSET));
qs.dwSize = sizeof (WSAQUERYSET);
qs.lpszServiceInstanceName = szName;
qs.lpServiceClassId = &guid;
qs.dwNameSpace = dwNameSpace;
qs.dwNumberOfProtocols = g_nMaxNumOfSocks;
qs.lpafpProtocols = afp;
SecureZeroMemory (abyBuf, sizeof (WSAQUERYSET) + OFFSET);
dwLength = sizeof(WSAQUERYSET) + OFFSET;
// some well known SAP name space services
if (nServiceType == 3 || nServiceType == 4 || nServiceType == 5 ||
nServiceType == 7 || nServiceType == 9 || nServiceType == 36 || nServiceType == 71)
fKnownSapId = TRUE;
if (WSALookupServiceBegin ( &qs,
LUP_RETURN_ADDR | LUP_RETURN_NAME,
&hLookup) == SOCKET_ERROR)
{
PrintError("WSALookupServiceBegin");
return;
}
printf ("Performing Query for service (type, name) = (%d, %s) . . .\n\n", nServiceType, pszServerName);
if (strcmp(pszServerName, "*") == 0)
{ // enumerate all service instances
for (;;)
{
dwResult = WSALookupServiceNext(hLookup,
0,
&dwLength,
pQS);
if (dwResult == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEFAULT)
{
printf("WSALookupServiceNext Error: Oops, we need a larger buffer size of : %d Bytes\n", dwLength);
}
else
{
PrintError("WSALookupServiceNext");
}
WSALookupServiceEnd (hLookup);
return;
}
if (!dwResult)
{
for (DWORD i = 0; i < pQS->dwNumberOfCsAddrs; i++)
{
SOCKADDR_STORAGE *mypt = (SOCKADDR_STORAGE *) pQS->lpcsaBuffer[i].RemoteAddr.lpSockaddr;
if (mypt)
{
// we have valid remote sockaddr
char temp[DEFAULT_STRING_LEN] = {'\0'};
printf ("Name[%d]: %30s", i, pQS->lpszServiceInstanceName);
GetSockAddrString (mypt, pQS->lpcsaBuffer[i].RemoteAddr.iSockaddrLength, temp, DEFAULT_STRING_LEN);
printf("%40s\n", temp);
if (! fKnownSapId)
ClientSend(&(pQS->lpcsaBuffer[i]));
}
}
}
}
}
else
{
dwResult = WSALookupServiceNext(hLookup,
0,
&dwLength,
pQS);
if (dwResult == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEFAULT)
{
printf("WSALookupServiceNext Error: Oops, we need a larger buffer size of : %d Bytes\n", dwLength);
}
else
{
PrintError("WSALookupServiceNext");
}
WSALookupServiceEnd (hLookup);
return;
}
if (!dwResult)
{
for (DWORD i = 0; i < pQS->dwNumberOfCsAddrs; i++)
{
SOCKADDR_STORAGE *mypt = (SOCKADDR_STORAGE *) pQS->lpcsaBuffer[i].RemoteAddr.lpSockaddr;
if (mypt)
{
// we have valid remote sockaddr
char temp[DEFAULT_STRING_LEN] = {'\0'};
printf ("Name[%d]: %30s", i, pQS->lpszServiceInstanceName);
GetSockAddrString (mypt, pQS->lpcsaBuffer[i].RemoteAddr.iSockaddrLength, temp, DEFAULT_STRING_LEN);
printf("%40s\n", temp);
if (! fKnownSapId)
ClientSend(&(pQS->lpcsaBuffer[i]));
}
}
}
WSALookupServiceEnd (hLookup);
}
}
//---------------------------------------------------------------------------
// FUNCTION: void ClientSend(CSADDR_INFO* lpcsaBuffer)
//
// PURPOSE: Given the server socket address info "lpcsaBuffer",
// send a message to the peer.
//---------------------------------------------------------------------------
void ClientSend(CSADDR_INFO* lpcsaBuffer)
{
SOCKADDR_STORAGE *mypt = (SOCKADDR_STORAGE *) lpcsaBuffer->RemoteAddr.lpSockaddr;
SOCKET s = INVALID_SOCKET;
static char szMessage[DEFAULT_STRING_LEN] = "A message from the client: ";
static BOOL fHostname = FALSE;
char szName[DEFAULT_STRING_LEN] = {'\0'};
unsigned long ulSize = sizeof(szName);
HRESULT hRet;
if (fHostname == FALSE)
{
GetComputerName(szName, &ulSize);
if (FAILED(hRet = StringCchCat(szMessage,
sizeof(szMessage)/sizeof(szMessage[0]),
szName
)))
{
printf("StringCchCat failed: 0x%x\n",hRet);
return;
}
fHostname = TRUE;
}
// The client doesn't need to know the detail of the addressing
// information, it can just do the common sequence of
// socket, connect, send/recv and closesocket by using the
// discovered RemoteAddr.
s = socket(lpcsaBuffer->RemoteAddr.lpSockaddr->sa_family,
lpcsaBuffer->iSocketType,
lpcsaBuffer->iProtocol);
if (s != INVALID_SOCKET)
{
if (connect(s, (SOCKADDR*)mypt, lpcsaBuffer->RemoteAddr.iSockaddrLength) != SOCKET_ERROR)
{
// NOTE on send on 64bit. strlen returns a 64bit INT and send takes a 32bit int.
// verify that your data is NOT larger that your data is not larger than SO_MAX_MSG_SIZE
// returned by getsockopt
if (send(s, szMessage, (INT)strlen(szMessage)+1, 0) != SOCKET_ERROR )
{
printf("send a message to the peer...\n");
}
else
{
printf("send failed %d\n", WSAGetLastError());
}
}
else
{
printf("connect failed %d\n", WSAGetLastError());
}
if (INVALID_SOCKET != s)
{
closesocket(s);
s = INVALID_SOCKET;
}
}
else
printf("Failed socket call %d\n", WSAGetLastError());
}
//---------------------------------------------------------------------------
// FUNCTION: void PrintError (char* errfunc)
// PURPOSE: Print error message.
//---------------------------------------------------------------------------
void PrintError (char* errfunc)
{
fflush (stdout);
if (WSASERVICE_NOT_FOUND == WSAGetLastError())
fprintf (stderr, "No matches found.\n");
else
if (WSA_E_NO_MORE == WSAGetLastError() || WSAENOMORE == WSAGetLastError())
fprintf (stderr, "No more matches. \n");
else
fprintf (stderr, "Function %s failed with error %d\n", errfunc, WSAGetLastError());
}
//---------------------------------------------------------------------------
// FUNCTION: void GetSockAddrString(SOCKADDR* pSAddr, int addrlen, char * dest, int destlen)
//
// PURPOSE: Converts INET or IPX address in "pSAddr" into ascii string in "dest"
//
//
// COMMENTS:
//
// INET address in decimal dotted notation:
// 107.50.104.97:4790
// IPX address format in hex:
// <8 char network address>.<12 char node address>.<4 char sock address>
//
// NOTE: Since Winsock 2 WSAAddressToString API is now only working for
// IP (AF_INET) and ATM (AF_ATM) address family, I would rather implement
// my version as GetSockAddrString()
//---------------------------------------------------------------------------
void GetSockAddrString(SOCKADDR_STORAGE* pSAddr, int addrlen, char * dest, int destlen)
{
HRESULT hRet;
if (pSAddr == NULL || dest == NULL)
return;
switch (pSAddr->ss_family)
{
case AF_INET:
case AF_INET6:
{
char host[NI_MAXHOST],
serv[NI_MAXSERV];
int hostlen = NI_MAXHOST,
servlen = NI_MAXSERV,
rc;
rc = getnameinfo((SOCKADDR*)pSAddr, addrlen, host, hostlen, serv, servlen,
NI_NUMERICHOST | NI_NUMERICSERV);
if (rc != 0)
{
if (FAILED(hRet = StringCchPrintf(dest,destlen-1,"getnameinfo failed: %d",rc)))
{
printf("StringCchPrintf failed: 0x%x\n",hRet);
return;
}
return;
}
if (FAILED(hRet = StringCchPrintf(dest,destlen-1,"%s:%s",host,serv)))
{
printf("StringCchPrintf failed: 0x%x\n",hRet);
return;
}
}
break;
case AF_IPX:
{
PSOCKADDR_IPX pIpxAddr = (PSOCKADDR_IPX) pSAddr;
if (FAILED(hRet = StringCchPrintf(dest, destlen-1,
"%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X:%04X",
(unsigned char)pIpxAddr->sa_netnum[0],
(unsigned char)pIpxAddr->sa_netnum[1],
(unsigned char)pIpxAddr->sa_netnum[2],
(unsigned char)pIpxAddr->sa_netnum[3],
(unsigned char)pIpxAddr->sa_nodenum[0],
(unsigned char)pIpxAddr->sa_nodenum[1],
(unsigned char)pIpxAddr->sa_nodenum[2],
(unsigned char)pIpxAddr->sa_nodenum[3],
(unsigned char)pIpxAddr->sa_nodenum[4],
(unsigned char)pIpxAddr->sa_nodenum[5],
ntohs(pIpxAddr->sa_socket)
)))
{
printf("StringCchPrintf failed: 0x%x\n",hRet);
return;
}
}
break;
default:
if(FAILED(hRet = StringCchCopy(dest,destlen-1,"Unknown socket address family")))
{
printf("StringCchCopy failed: 0x%x\n",hRet);
}
break;
}
}
void SetIpPort(SOCKADDR_STORAGE *dest, SOCKADDR_STORAGE *src)
{
if (dest->ss_family == AF_INET)
((SOCKADDR_IN *)dest)->sin_port = ((SOCKADDR_IN *)src)->sin_port;
else if (dest->ss_family == AF_INET6)
((SOCKADDR_IN6 *)dest)->sin6_port = ((SOCKADDR_IN6 *)src)->sin6_port;
}
void DumpServiceClassInfo(LPWSASERVICECLASSINFO lpSci)
{
WCHAR szGuid[MAX_PATH] = {'\0'};
GUID TempGuid = {0};
DWORD i = 0;
HRESULT hRet;
if (NULL == lpSci)
return;
CopyMemory(&TempGuid,lpSci->lpServiceClassId,sizeof(GUID));
if (FAILED(hRet = StringCchPrintfW(szGuid,
sizeof(szGuid)/sizeof(szGuid[0]),
L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
TempGuid.Data1,
TempGuid.Data2,
TempGuid.Data3,
TempGuid.Data4[0],
TempGuid.Data4[1],
TempGuid.Data4[2],
TempGuid.Data4[3],
TempGuid.Data4[4],
TempGuid.Data4[5],
TempGuid.Data4[6],
TempGuid.Data4[7]
)))
{
printf("StringCchPrintfW failed: 0x%x\n",hRet);
return;
}
printf("\nWSASERVICECLASSINFO %p:\n",lpSci);
wprintf(L"lpServiceClassId:\t%s\n",szGuid);
printf("lpszServiceClassName:\t%s\n",lpSci->lpszServiceClassName);
printf("dwCount:\t\t%d\n",lpSci->dwCount);
for (i = 0; i < lpSci->dwCount; i++)
{
printf("lpClassInfos (WSANSCLASSINFOW %p):\n",&lpSci->lpClassInfos[i]);
printf("\t\tdwNameSpace:\t%d\n",lpSci->lpClassInfos[i].dwNameSpace);
printf("\t\tdwValueSize:\t%d\n",lpSci->lpClassInfos[i].dwValueSize);
printf("\t\tdwValueType:\t%d\n",lpSci->lpClassInfos[i].dwValueType);
printf("\t\tlpszName:\t%s\n",lpSci->lpClassInfos[i].lpszName);
printf("\t\tlpValue:\t%p\n",lpSci->lpClassInfos[i].lpValue);
}
printf("\n");
return;
}