< Register and install services | RnR Main | Query A Service & Form A Query >
What do we have in this chapter 8 part 3?
|
Service Registration Example
In this section, we'll show you how to register your own service under both the SAP and NTDS name spaces. The Windows NT domain space is quite powerful, which is why we want to include it in our example. However, there are a few features to be aware of before you begin. First, the Windows NT domain space requires Windows 2000 or Windows XP because it is based on the Active Directory directory service. This also means that the Windows 2000 or Windows XP workstation on which you hope to register and/or look up services must have a machine account in that domain in order to access Active Directory. The other feature to note is that the Windows NT domain space is capable of registering socket addresses from any protocol family. Your IP and IPX services can all be registered in the same name space. Also, there is a dynamic way of adding and removing IP-based services. The following code example illustrates the basic steps required to register an instance of a service. For simplicity, no error checking is performed.
SOCKET socks[2]; WSAQUERYSET qs; CSADDR_INFO lpCSAddr[2]; SOCKADDR_IN sa_in; SOCKADDR_IPX sa_ipx; IPX_ADDRESS_DATA ipx_data; GUID guid = SVCID_NETWARE(200); int ret, cb;
memset(&qs, 0, sizeof(WSAQUERYSET)); qs.dwSize = sizeof(WSAQUERYSET); qs.lpszServiceInstanceName = (LPSTR)"Widget Server"; qs.lpServiceClassId = &guid; qs.dwNameSpace = NS_ALL; qs.lpNSProviderId = NULL; qs.lpcsaBuffer = lpCSAddr; qs.lpBlob = NULL; |
// Set the IP address of our service
memset(&sa_in, 0, sizeof(sa_in));
sa_in.sin_family = AF_INET;
sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
sa_in.sin_port = 5150;
socks[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
ret = bind(socks[0], (SOCKADDR *)&sa_in, sizeof(sa_in));
cb = sizeof(sa_in);
getsockname(socks[0], (SOCKADDR *)&sa_in, &cb);
lpCSAddr[0].iSocketType = SOCK_DGRAM;
lpCSAddr[0].iProtocol = IPPROTO_UDP;
lpCSAddr[0].LocalAddr.lpSockaddr = (SOCKADDR *)&sa_in;
lpCSAddr[0].LocalAddr.iSockaddrLength = sizeof(sa_in);
lpCSAddr[0].RemoteAddr.lpSockaddr = (SOCKADDR *)&sa_in;
lpCSAddr[0].RemoteAddr.iSockaddrLength = sizeof(sa_in);
// Set up the IPX address for our service
memset(sa_ipx.sa_netnum, 0, sizeof(sa_ipx.sa_netnum));
memset(sa_ipx.sa_nodenum, 0, sizeof(sa_ipx.sa_nodenum));
sa_ipx.sa_family = AF_IPX;
sa_ipx.sa_socket = 0;
socks[1] = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
ret = bind(socks[1], (SOCKADDR *)&sa_ipx, sizeof(sa_ipx));
cb = sizeof(IPX_ADDRESS_DATA);
memset (&ipx_data, 0, cb);
ipx_data.adapternum = 0;
ret = getsockopt(socks[1], NSPROTO_IPX, IPX_ADDRESS, (char *)&ipx_data, &cb);
cb = sizeof(SOCKADDR_IPX);
getsockname(socks[1], (SOCKADDR *)sa_ipx, &cb);
memcpy(sa_ipx.sa_netnum, ipx_data.netnum, sizeof(sa_ipx.sa_netnum));
memcpy(sa_ipx.sa_nodenum, ipx_data.nodenum, sizeof(sa_ipx.sa_nodenum));
lpCSAddr[1].iSocketType = SOCK_DGRAM;
lpCSAddr[1].iProtocol = NSPROTO_IPX;
lpCSAddr[1].LocalAddr.lpSockaddr = (struct sockaddr *)&sa_ipx;
lpCSAddr[1].LocalAddr.iSockaddrLength = sizeof(sa_ipx);
lpCSAddr[1].RemoteAddr.lpSockaddr = (struct sockaddr *)&sa_ipx;
lpCSAddr[1].RemoteAddr.iSockaddrLength = sizeof(sa_ipx);
qs.dwNumberOfCsAddrs = 2;
WSASetService(&qs, RNRSERVICE_REGISTER, 0L);
The example illustrates how to set an instance of a service so that a client of that service can find out the address that it needs to communicate with the service. The first order of business is to initialize the WSAQUERYSET structure. We also need to give a name to the instance of our service. In this case, we simply call it “Widget Server.” The other critical step is to use the same GUID we used to register our service class. Whenever you register an instance of a service, that service must belong to a service class. In this case, we use the “Widget Service Class” (defined in the previous section), whose GUID is SVCID_NETWARE(200). The next step is to set the name spaces that we are interested in. Because our service runs over both IPX and UDP, we specify NS_ALL. Because we're specifying a preexisting name space, lpNSProviderId must be set to NULL.
The next step is to set up the SOCKADDR structures within the CSADDR_INFO array that WSASetService() passes as the lpcsaBuffer field of the WSAQUERYSET structure. You'll notice that in our example we actually create the sockets and bind them to a local address before we set up the SOCKADDR structure. This is because we need to find the exact local address that clients need to connect to. For example, when creating our UDP socket for the server, we bind to INADDR_ANY, which doesn't give us the actual IP address until we call getsockname. Using the information returned from getsockname(), we can build a SOCKADDR_IN structure. Within the CSADDR_INFO structure, we set the socket type and the protocol. The other two fields are the local and remote address information. The local address is the address that a server should bind to, and the remote address is the address that a client should use to connect to the service.
After setting up the SOCKADDR_IN structure for our UDP-based server, we set up the IPX-based service. In Chapter 4, you saw that servers should bind to the internal network number by setting the network and node number to 0. Again, this doesn't give you the address that clients need, so call the socket option IPX_ADDRESS to get the actual address. In filling the CSADDR_INFO structure for IPX, use SOCK_DGRAM and NSPROTO_IPX for the socket type and the protocol, respectively. The last step is to set the dwNumberOfCsAddrs field in the WSAQUERYSET structure to 2 because there are two addresses, UDP and IPX, that clients can use to establish a connection. Finally, call WSASetService() with our WSAQUERYSET structure, the RNRSERVICE_REGISTER flag, and no control flags. You do not specify the SERVICE_MULTIPLE control flag so that if you choose to deregister our service, all instances of the service (both the IPX and UDP addresses) will be deregistered.
There is one consideration that the example does not take into account: multihomed machines. If you create a UDP-based server that binds to INADDR_ANY on a multihomed machine, the client can connect to the server on any of the available interfaces. With IP, getsockname is not sufficient; you must obtain all of the local IP interfaces. There are a number of methods for getting this information, depending on the platform you are on. One method common to all platforms is calling gethostbyname() to return a list of IP addresses for our name. Under Winsock 2, you can also call the ioctl command SIO_GET_INTERFACE_LIST. For Windows 2000 and Windows XP, the ioctl SIO_ADDRESS_LIST_QUERY is available. The following example demonstrates how to advertise, install and register services. Create a new empty Win32 console application project and add the project/solution name.
Add the following code.
// Description:
// This sample illustrates how a server can create a socket and
// advertise the existence of the service to clients. It also
// shows how a client with knowledge of the server's name only
// can find out how to connect to the server in order to transmit
// data. The server accomplishes this by installing a service
// class which describes the basic characteristics of the service
// and then registering an instance of the server which references
// the given class. The service can be registered for multiple
// name spaces such as IPX and NTDS.
//
// The client can query the available name spaces for knowledge
// of a service of the given name. If the query completes
// successfully, the address of the service is returned. All
// the client needs to do is use that address in either a connect or sendto call.
//
// Command line arguments
// rnrcsexample -c:ServiceName -s:ServiceName -t:ID -n:NameSpace
// -c:ServiceName Act as client and query for this service name
// -s:ServiceName Act as server and register service as this name
// -t:ID SAP ID to register under
// -n:NameSpace Name space to register service under
// Supported namespaces are NS_ALL, NS_SAP, NS_NTDS
//
// Link to ws2_32.lib
#include <winsock2.h>
// windows.h should be after winsock2.h
#include <windows.h>
#include <ws2tcpip.h>
#include <svcguid.h>
#include <wsipx.h>
#include <wsnwlink.h>
#include <nspapi.h>
#include <stdio.h>
#define MAX_NUM_CSADDRS 20
#define MAX_INTERFACE_LIST 20
#define MAX_NUM_SOCKETS 20
#define MAX_BUFFER_SZ 512
#define MAX_SERVER_NAME_SZ 64
#define MAX_GUID_SZ 256
#define DEFAULT_SERVER_NAME "GedikServer"
char szServerName[MAX_SERVER_NAME_SZ]; // Server name
BOOL bServer; // Client or server?
DWORD dwNameSpace, // Name space to register on
dwSapId; // Used to create a GUID
SOCKET socks[MAX_NUM_SOCKETS]; // Socket handles for our server
HANDLE hEvents[MAX_NUM_SOCKETS];
BOOL ClientSend(int socktype, int proto, SOCKADDR *server, int len, int count);
char *FormatIPXAddr(SOCKADDR_IPX *addr);
// Function: usage
// Description: Print usage information.
void usage(char *progname)
{
printf("Usage: %s -c:ServiceName -s:ServiceName -t:ID -n:[NS_ALL|NS_NTDS|NS_SAP]\n", progname);
printf("Server:\n");
printf(" -s:ServiceName Act as server and register service as this name\n");
printf(" -t:ID SAP ID to register under\n");
printf(" This is arbitrary, just needed to create a GUID\n");
printf(" -n:NameSpace Name space to register service under\n");
printf(" Can be one of the strings: NS_ALL, NS_NTDS, or NS_SAP\n");
printf("Client:\n");
printf(" -c:ServiceName Act as client and query for this service name\n");
ExitProcess(-1);
}
// Function: ValidateArgs
// Description: Parse command line parameters and set some global variables used by the application.
void ValidateArgs(int argc, char **argv)
{
int i;
// Set some default values
strcpy_s(szServerName, sizeof(szServerName), DEFAULT_SERVER_NAME);
dwNameSpace = NS_ALL;
dwSapId = 200;
for(i=0; i < MAX_NUM_SOCKETS ;i++)
socks[i] = SOCKET_ERROR;
for (i=1; i < argc ;i++)
{
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
{
switch (tolower(argv[i][1]))
{
case 't': // SAP id used to generate GUID
if (strlen(argv[i]) > 3)
dwSapId = atoi(&argv[i][3]);
break;
case 'c': // Client, query for given service
bServer = FALSE;
if (strlen(argv[i]) > 3)
strcpy_s(szServerName, sizeof(szServerName), &argv[i][3]);
break;
case 's': // Server, register as the given service
bServer = TRUE;
if (strlen(argv[i]) > 3)
strcpy_s(szServerName, sizeof(szServerName), &argv[i][3]);
break;
case 'n': // Name spaces to register service under
if (!_strnicmp("NS_NTDS", &argv[i][3], strlen("NS_NTDS")))
dwNameSpace = NS_NTDS;
else if (!_strnicmp("NS_SAP", &argv[i][3], strlen("NS_SAP")))
dwNameSpace = NS_SAP;
break;
default:
usage(argv[0]);
return;
}
}
else
usage(argv[0]);
}
return;
}
// Function: EnumNameSpaceProviders
// Description:
// Returns an array of those name spaces which our application
// supports. If one day, NS_DNS becomes dynamic, modify this
// function to return that structure as well.
WSANAMESPACE_INFO *EnumNameSpaceProviders(int *count)
{
WSANAMESPACE_INFO *nsinfo, *nsinfocpy;
BYTE *pbuf=NULL, *pbufcpy=NULL;
// WCHAR szGuid[MAX_GUID_SZ];
DWORD dwBytes;
int i, j, ret;
// int h;
*count = 0;
dwBytes = 0;
// First find out how big of a buffer we need
ret = WSAEnumNameSpaceProviders(&dwBytes, NULL);
if (ret == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAEFAULT)
{
printf("WSAEnumNameSpaceProviders() failed with error code %d\n", WSAGetLastError());
return NULL;
}
}
else
printf("WSAEnumNameSpaceProviders() should be fine!\n");
// Allocate this buffer and call the function again
pbuf = (BYTE *)GlobalAlloc(GPTR, dwBytes);
if (!pbuf)
{
printf("GlobaAlloc() for pbuf buffer failed with error code %d\n", GetLastError());
return NULL;
}
else
printf("GlobaAlloc() for pbuf buffer is OK!\n");
nsinfo = (WSANAMESPACE_INFO *)pbuf;
ret = WSAEnumNameSpaceProviders(&dwBytes, nsinfo);
if (ret == SOCKET_ERROR)
{
printf("WSAEnumNameSpaceProviders() failed with error code %d\n", WSAGetLastError());
HeapFree(GetProcessHeap(), 0, pbuf);
return NULL;
}
else
printf("WSAEnumNameSpaceProviders() is OK!\n");
// Make a copy buffer which we will return our data in
pbufcpy = GlobalAlloc(GPTR, dwBytes);
if (!pbufcpy)
{
printf("GlobaAlloc() for copy buffer failed with error code %d\n", GetLastError());
return NULL;
}
else
printf("GlobaAlloc() for copy buffer is OK!\n");
// Just for informational
/*for(h=0; h < ret ;h++)
{
printf("\nName Space: %S\n", nsinfo[h].lpszIdentifier);
printf(" ID: ");
switch (nsinfo[h].dwNameSpace)
{
case NS_ALL:
printf("NS_ALL\n");
break;
case NS_SAP:
printf("NS_SAP\n");
break;
case NS_NDS:
printf("NS_NDS\n");
break;
case NS_PEER_BROWSE:
printf("NS_PEER_BROWSE\n");
break;
case NS_TCPIP_LOCAL:
printf("NS_TCPIP_LOCAL\n");
break;
case NS_TCPIP_HOSTS:
printf("NS_TCPIP_HOSTS\n");
break;
case NS_DNS:
printf("NS_DNS\n");
break;
case NS_NETBT:
printf("NS_NETBT\n");
break;
case NS_WINS:
printf("NS_WINS\n");
break;
case NS_NBP:
printf("NS_NBP\n");
break;
case NS_MS:
printf("NS_MS\n");
break;
case NS_STDA:
printf("NS_STDA\n");
break;
case NS_NTDS:
printf("NS_NTDS\n");
break;
case NS_X500:
printf("NS_X500\n");
break;
case NS_NIS:
printf("NS_NIS\n");
break;
case NS_NISPLUS:
printf("NS_NISPLUS\n");
break;
case NS_WRQ:
printf("NS_WRQ\n");
break;
default:
printf("%d\n", nsinfo[h].dwNameSpace);
}
StringFromGUID2(&nsinfo[h].NSProviderId, szGuid, MAX_GUID_SZ);
printf(" GUID: %S\n", szGuid);
printf(" Active: %s\n", ((nsinfo[h].fActive) ? "YES" : "NO"));
printf(" Version: %d\n", nsinfo[h].dwVersion);
}*/
// Loop through the returned name space structures picking
// those which our app supports
nsinfocpy = (WSANAMESPACE_INFO *)pbufcpy;
printf("Well, %d name spaces are available\n", ret);
j = 0;
for(i=0; i < ret ;i++)
{
switch (nsinfo[i].dwNameSpace)
{
// Currently we only support SAP or NTDS name spaces
// so ignore anything else (like DNS)
case NS_SAP:
case NS_NTDS:
memcpy(&nsinfocpy[j++], &nsinfo[i], sizeof(WSANAMESPACE_INFO));
break;
}
}
// Free the original buffer and return our copy of useable name spaces
GlobalFree(pbuf);
printf("Found %d useable name spaces\n", j);
*count = j;
return nsinfocpy;
}
// Function: InstallServiceClass
// Description:
// This function installs the service class which is required before
// registering an instance of a service. A service class defines the
// generic attributes of a class of services such as whether it is
// connection oriented or not as well as what protocols is supports (IPX, IP, etc).
BOOL InstallServiceClass(GUID *svcguid, WSANAMESPACE_INFO *nsinfo, int nscount)
{
WSASERVICECLASSINFO sci;
WSANSCLASSINFO *nsclass=NULL;
DWORD dwZero=0, dwUdpPort=0;
char szServiceClassName[64];
int i, ret;
// Generate a name of our service class
wsprintf((LPWSTR)szServiceClassName, L"ServiceClass: %003d", dwSapId);
printf("Installing service class: '%S'\n", szServiceClassName);
// There are 2 attributes we need to set for every name space
// we want to register in: Connection Oriented/Connectionless as
// well as the address family of the protocols we support.
nsclass = GlobalAlloc(GPTR, sizeof(WSANSCLASSINFO) * nscount * 2);
if (!nsclass)
{
printf("GlobalAlloc() for WSANSCLASSINFO failed with error code %d\n", GetLastError());
return FALSE;
}
else
printf("GlobalAlloc() for WSANSCLASSINFO should be fine!\n");
// Initialize the structure
memset(&sci, 0, sizeof(sci));
sci.lpServiceClassId = svcguid;
sci.lpszServiceClassName = (LPWSTR)szServiceClassName;
sci.dwCount = nscount * 2;
sci.lpClassInfos = nsclass;
for(i=0; i < nscount ;i++)
{
// No matter what name space we use we set the connection
// oriented attribute to false (i.e. connectionless)
nsclass[i*2].lpszName = SERVICE_TYPE_VALUE_CONN;
nsclass[i*2].dwNameSpace = nsinfo[i].dwNameSpace;
nsclass[i*2].dwValueType = REG_DWORD;
nsclass[i*2].dwValueSize = sizeof(DWORD);
nsclass[i*2].lpValue = &dwZero;
if (nsinfo[i].dwNameSpace == NS_NTDS)
{
// If NS_NTDS is available we will be running a UDP
// based service on the given port number
printf("Setting NS_NTDS info...\n");
nsclass[(i*2)+1].lpszName = SERVICE_TYPE_VALUE_UDPPORT;
nsclass[(i*2)+1].dwNameSpace = nsinfo[i].dwNameSpace;
nsclass[(i*2)+1].dwValueType = REG_DWORD;
nsclass[(i*2)+1].dwValueSize = sizeof(DWORD);
nsclass[(i*2)+1].lpValue = &dwUdpPort;
}
else if (nsinfo[i].dwNameSpace == NS_SAP)
{
// If NS_SAP is available we will run an IPX based
// service on the given SAP ID
printf("Setting NS_SAP info...\n");
nsclass[(i*2)+1].lpszName = SERVICE_TYPE_VALUE_SAPID;
nsclass[(i*2)+1].dwNameSpace = nsinfo[i].dwNameSpace;
nsclass[(i*2)+1].dwValueType = REG_DWORD;
nsclass[(i*2)+1].dwValueSize = sizeof(DWORD);
nsclass[(i*2)+1].lpValue = &dwSapId;
}
}
// Install the service class
ret = WSAInstallServiceClass(&sci);
if (ret == SOCKET_ERROR)
{
printf("WSAInstallServiceClass() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else
printf("WSAInstallServiceClass() is OK!\n");
GlobalFree(nsclass);
return TRUE;
}
// Function: GetIPInterfaceList
// Description:
// This function returns an array of SOCKADDR_IN structures,
// one for every local IP interface on the machine. We use
// the ioctl command SIO_GET_INTERFACE_LIST to do this although
// we could have used any number of method such as gethostbyname(),
// SIO_INTERFACE_LIST_QUERY, or the IP helper APIs.
SOCKADDR_IN *GetIPInterfaceList(SOCKET s, int *count)
{
static SOCKADDR_IN sa_in[MAX_NUM_CSADDRS];
INTERFACE_INFO iflist[MAX_INTERFACE_LIST];
DWORD dwBytes;
int ret, i;
*count = 0;
ret = WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &iflist, sizeof(iflist), &dwBytes, NULL, NULL);
if (ret == SOCKET_ERROR)
{
printf("WSAIoctl(SIO_GET_INTERFACE_LIST) failed with error code %d\n", WSAGetLastError());
return NULL;
}
else
printf("WSAIoctl(SIO_GET_INTERFACE_LIST) is OK!\n");
// Loop through the interfaces and copy them into the SOCKADDR_IN array
*count = dwBytes / sizeof(INTERFACE_INFO);
for(i=0; i < *count ;i++)
{
memcpy(&sa_in[i], &iflist[i].iiAddress.AddressIn, sizeof(SOCKADDR_IN));
}
return sa_in;
}
// Function: GetIPXInterfaceList
// Description:
// This function enumerates the local IPX interfaces and returns
// them in an array of SOCKADDR_IPX structures. The only way to
// do this is to use the socket option IPX_MAX_ADAPTER_NUM to
// find out how many interfaces there are and then call
// IPX_ADDRESS on each one to find the address.
SOCKADDR_IPX *GetIPXInterfaceList(SOCKET s, int *count)
{
static SOCKADDR_IPX sa_ipx[MAX_NUM_CSADDRS];
IPX_ADDRESS_DATA ipxdata;
int ifcount, nSize, ret, i;
*count = 0;
ifcount = 0;
// Find out how many IPX interfaces are available
nSize = sizeof(ifcount);
ret = getsockopt(s, NSPROTO_IPX, IPX_MAX_ADAPTER_NUM, (char *)&ifcount, &nSize);
if (ret == SOCKET_ERROR)
{
printf("getsockopt(IPX_MAX_ADAPTER_NUM) failed with error code %d\n", WSAGetLastError());
return NULL;
}
else
printf("getsockopt(IPX_MAX_ADAPTER_NUM) is OK!\n");
// Loop through and get the address of each interface
for(i=0; i < ifcount ;i++)
{
nSize = sizeof(ipxdata);
memset(&ipxdata, 0, sizeof(ipxdata));
ipxdata.adapternum = i;
ret = getsockopt(s, NSPROTO_IPX, IPX_ADDRESS, (char *)&ipxdata, &nSize);
if (ret == SOCKET_ERROR)
{
printf("getsockopt(IPX_ADDRESS) failed with error code %d\n", WSAGetLastError());
continue;
}
// Make sure that this interface is actually up and running
if (ipxdata.status == TRUE)
{
memcpy(&sa_ipx[i].sa_netnum, &ipxdata.netnum, sizeof(ipxdata.netnum));
memcpy(&sa_ipx[i].sa_nodenum, &ipxdata.nodenum, sizeof(ipxdata.nodenum));
}
}
*count = ifcount;
return sa_ipx;
}
// Function: Advertise
// Description:
// This function advertises an instance of the server. This
// function also creates the server for each available name
// space. To advertise you need all the local interfaces that
// the client can connect the to the server. This is done
// by filling out a WSAQUERYSET structure along with the
// appropriate CSADDR_INFO structures. The CSADDR_INFO
// structures define the interfaces the service is listening on.
BOOL Advertise(GUID *guid, WSANAMESPACE_INFO *nsinfo, int nscount, char *servicename)
{
WSAQUERYSET qs;
CSADDR_INFO csaddrs[MAX_NUM_CSADDRS];
int ret,i, j, iSize, addrcnt;
// Initialize the WSAQUERYSET structure
memset(&qs, 0, sizeof(WSAQUERYSET));
qs.dwSize = sizeof(WSAQUERYSET);
qs.lpszServiceInstanceName = (LPWSTR)servicename;
qs.lpServiceClassId = guid;
qs.dwNameSpace = NS_ALL;
qs.lpNSProviderId = NULL;
qs.lpcsaBuffer = csaddrs;
qs.lpBlob = NULL;
addrcnt=0;
// For each valid name space we create an instance of the
// service and find out what local interfaces are available
// that the client can connect to and communicate with the server.
for (i=0; i < nscount ;i++)
{
if (nsinfo[i].dwNameSpace == NS_NTDS)
{
SOCKADDR_IN localip;
SOCKADDR_IN *iflist=NULL;
int ipifcount;
// Create a UDP based server
printf("Setting up NTDS entry...\n");
socks[i] = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (socks[i] == INVALID_SOCKET)
{
printf("WSASocket() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else
printf("WSASocket() is fine!\n");
localip.sin_family = AF_INET;
localip.sin_port = htons(0);
localip.sin_addr.s_addr = htonl(INADDR_ANY);
ret = bind(socks[i], (SOCKADDR *)&localip, sizeof(localip));
if (ret == SOCKET_ERROR)
{
printf("bind() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else
printf("bind() should be fine lol!\n");
// Get the port number back since we don't specifically give one
iSize = sizeof(localip);
ret = getsockname(socks[i], (SOCKADDR *)&localip, &iSize);
if (ret == SOCKET_ERROR)
{
printf("getsockname(IP) failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else
printf("getsockname(IP) is pretty fine!\n");
// Get a list of the IP interfaces
iflist = GetIPInterfaceList(socks[i], &ipifcount);
if (!iflist)
{
printf("Unable to enumerate IP interfaces!\n");
return FALSE;
}
else
printf("Enumerate IP interfaces is OK!\n");
// Fill out the CSADDR_INFO structures with each IP interface
for (j=0; j < ipifcount ;j++)
{
iflist[j].sin_family = AF_INET;
iflist[j].sin_port = localip.sin_port;
csaddrs[addrcnt].iSocketType = SOCK_DGRAM;
csaddrs[addrcnt].iProtocol = IPPROTO_UDP;
csaddrs[addrcnt].LocalAddr.lpSockaddr = (SOCKADDR *)&iflist[j];
csaddrs[addrcnt].LocalAddr.iSockaddrLength = sizeof(iflist[j]);
csaddrs[addrcnt].RemoteAddr.lpSockaddr = (SOCKADDR *)&iflist[j];
csaddrs[addrcnt].RemoteAddr.iSockaddrLength = sizeof(iflist[j]);
printf("\t[%d] Local IP [%s:%d]\n", j,
inet_ntoa(((SOCKADDR_IN *)(csaddrs[addrcnt].LocalAddr.lpSockaddr))->sin_addr),
ntohs(((SOCKADDR_IN *)(csaddrs[addrcnt].LocalAddr.lpSockaddr))->sin_port));
printf("\t[%d] Remote IP [%s:%d]\n", j,
inet_ntoa(((SOCKADDR_IN *)(csaddrs[addrcnt].RemoteAddr.lpSockaddr))->sin_addr),
ntohs(((SOCKADDR_IN *)(csaddrs[addrcnt].RemoteAddr.lpSockaddr))->sin_port));
addrcnt++;
}
}
else if (nsinfo[i].dwNameSpace == NS_SAP)
{
SOCKADDR_IPX localipx, *ipxlist;
int ipxifcount;
// Create an instance of our IPX server
printf("Setting up IPX entry...\n");
socks[i] = WSASocket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX, NULL, 0, WSA_FLAG_OVERLAPPED);
if (socks[i] == INVALID_SOCKET)
{
printf("WSASocket() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else
printf("WSASocket() is OK!\n");
memset(&localipx, 0, sizeof(SOCKADDR_IPX));
localipx.sa_family = AF_IPX;
ret = bind(socks[i], (SOCKADDR *)&localipx, sizeof(localipx));
if (ret == SOCKET_ERROR)
{
printf("bind() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else
printf("bind() is pretty fine!\n");
// Find out the socket number that the IPX server is listening
// on since we didn't explicitly specify one.
iSize = sizeof(localipx);
ret = getsockname(socks[i], (SOCKADDR *)&localipx, &iSize);
if (ret == SOCKET_ERROR)
{
printf("getsockname(IPX) failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else
printf("getsockname(IPX) is OK!\n");
// Enumerate the local IPX interfaces on which clients can connect to us
ipxlist = GetIPXInterfaceList(socks[i], &ipxifcount);
if (!ipxlist)
{
printf("Unable to get the IPX interface list!\n");
return FALSE;
}
else
printf("Getting the IPX interface list is OK!\n");
printf("Number IPX interfaces: %d\n", ipxifcount);
for(j=0; j < ipxifcount ;j++)
{
ipxlist[j].sa_family = AF_IPX;
ipxlist[j].sa_socket = localipx.sa_socket;
csaddrs[addrcnt].iSocketType = SOCK_DGRAM;
csaddrs[addrcnt].iProtocol = NSPROTO_IPX;
csaddrs[addrcnt].LocalAddr.lpSockaddr = (SOCKADDR *)&ipxlist[j];
csaddrs[addrcnt].LocalAddr.iSockaddrLength = sizeof(ipxlist[j]);
csaddrs[addrcnt].RemoteAddr.lpSockaddr = (SOCKADDR *)&ipxlist[j];
csaddrs[addrcnt].RemoteAddr.iSockaddrLength = sizeof(ipxlist[j]);
printf("\t[%d] Local IPX [%S]\n", j, FormatIPXAddr((SOCKADDR_IPX *)(csaddrs[addrcnt].LocalAddr.lpSockaddr)));
printf("\t[%d] Remote IPX [%S]\n", j, FormatIPXAddr((SOCKADDR_IPX *)(csaddrs[addrcnt].RemoteAddr.lpSockaddr)));
addrcnt++;
}
}
// Create an event for the server to use with WSAEventSelect
hEvents[i] = WSACreateEvent();
if (hEvents[i] == WSA_INVALID_EVENT)
{
printf("WSACreateEvent() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else
printf("WSACreateEvent() is pretty fine!\n");
}
qs.dwNumberOfCsAddrs = addrcnt;
// Register our service(s)
ret = WSASetService(&qs, RNRSERVICE_REGISTER, 0L);
if (ret == SOCKET_ERROR)
{
printf("WSASetService() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
printf("WSASetService() succeeded!!!\n");
return TRUE;
}
// Function: LookupService
// Description:
// This function queries for an instance of the given service
// running on the network. You can either query for a specific
// service name or specify the wildcard string "*". If an instance
// is found, send some data to it.
void LookupService(GUID *guid, int sapid, int ns, char *servername)
{
WSAQUERYSET qs, *pqs = NULL;
AFPROTOCOLS afp[2] = {{AF_IPX, NSPROTO_IPX}, {AF_INET, IPPROTO_UDP}}, afpx[1] = {{AF_IPX, NSPROTO_IPX}};
char querybuf[sizeof(WSAQUERYSET) + 4096];
DWORD nSize = sizeof(WSAQUERYSET) + 4096, i, j = 0, ret;
HANDLE hLookup;
// Initialize the WSAQUERYSET structure
memset(&qs, 0, sizeof(WSAQUERYSET));
qs.dwSize = sizeof(WSAQUERYSET);
qs.lpszServiceInstanceName = (LPWSTR)servername;
qs.lpServiceClassId = guid;
qs.dwNameSpace = NS_SAP;
qs.dwNumberOfProtocols = 1;
qs.lpafpProtocols = afpx;
// Begin the lookup. We want the name and address back
ret = WSALookupServiceBegin(&qs, LUP_RETURN_ADDR | LUP_RETURN_NAME, &hLookup);
if (ret != SOCKET_ERROR)
{
printf("WSALookupServiceBegin() is pretty fine!\n");
// This is quite weird for Win 2000
pqs = (WSAQUERYSET *) malloc(sizeof(querybuf));
nSize = sizeof(WSAQUERYSET) + 4096;
memset(querybuf, 0, nSize);
do
{
if (WSALookupServiceNext(hLookup, 0, &nSize, pqs) != 0)
{
ret = WSAGetLastError();
printf(" WSALookupServiceNext() #%d return value is = %d\n", j, ret);
}
else
{
printf("WSALookupServiceNext() #%d is OK!\n", j);
ret = 0;
}
j++;
if (ret == WSAEFAULT)
{
if (pqs)
free(pqs);
pqs = (WSAQUERYSET *) malloc(nSize);
if (!pqs)
{
printf("Could not allocate memory: %d\n", GetLastError());
break;
}
else
{
printf("Memory allocation should be fine for pqs\n");
}
}
// Some testing...look like failed lor, weird...weird
if ((ret == 0) && (pqs))
printf("Service instance name is %S\n", pqs->lpszServiceInstanceName);
} while ((ret != WSA_E_NO_MORE) && (ret != WSAENOMORE));
}
else
{
printf("Error on WSALookupServiceBegin() %d\n", WSAGetLastError());
}
// This part also weirdddddddddddddd....
// First attempt for WSALookupServiceNext() already failed? :-(
if(pqs->lpszServiceInstanceName != NULL)
{
printf("pqs->lpszServiceInstanceName is not NULL\n");
// Assuming that we've found a server out there, print some info and send some data to it
printf("Found service: %s\n", pqs->lpszServiceInstanceName);
printf("Returned %d CSADDR structures\n", pqs->dwNumberOfCsAddrs);
for(i=0; i < pqs->dwNumberOfCsAddrs ;i++)
{
switch (pqs->lpcsaBuffer[i].iProtocol)
{
case IPPROTO_UDP:
printf("IPPROTO_UDP\n");
((SOCKADDR_IN *)pqs->lpcsaBuffer[i].RemoteAddr.lpSockaddr)->sin_family = AF_INET;
break;
case NSPROTO_IPX:
printf("NSPROTO_IPX\n");
((SOCKADDR_IPX *)pqs->lpcsaBuffer[i].RemoteAddr.lpSockaddr)->sa_family = AF_IPX;
printf("\t[%d] Local IPX [%s]\n", i, FormatIPXAddr((SOCKADDR_IPX *)(pqs->lpcsaBuffer[i].LocalAddr.lpSockaddr)));
printf("\t[%d] Remote IPX [%s]\n", i, FormatIPXAddr((SOCKADDR_IPX *)(pqs->lpcsaBuffer[i].RemoteAddr.lpSockaddr)));
break;
default:
printf("Unknown!\n");
break;
}
// Send data
ClientSend(pqs->lpcsaBuffer[i].iSocketType, pqs->lpcsaBuffer[i].iProtocol,
(SOCKADDR *)pqs->lpcsaBuffer[i].RemoteAddr.lpSockaddr,
pqs->lpcsaBuffer[i].RemoteAddr.iSockaddrLength, 4);
}
}
else
{
printf("\npqs->lpszServiceInstanceName is NULL\n");
printf("Found service: %s\n", qs.lpszServiceInstanceName);
printf("Returned %d CSADDR structures\n", qs.dwNumberOfCsAddrs);
// More action here!
}
free(pqs);
WSALookupServiceEnd(hLookup);
return;
}
// Function: ClientSend
// Description: Create a socket of the given type and send some data to it
BOOL ClientSend(int socktype, int proto, SOCKADDR *server, int len, int count)
{
WSABUF wbuf;
char sndbuf[MAX_BUFFER_SZ];
DWORD dwBytesSent;
int i, ret;
if (proto == NSPROTO_IPX)
{
socks[0] = WSASocket(AF_IPX, socktype, proto, NULL, 0, WSA_FLAG_OVERLAPPED);
if (socks[0] == INVALID_SOCKET)
{
printf("WSASocket() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else
printf("WSASocket() is OK!\n");
}
else if (proto == IPPROTO_UDP)
{
socks[0] = WSASocket(AF_INET, socktype, proto, NULL, 0, WSA_FLAG_OVERLAPPED);
if (socks[0] == INVALID_SOCKET)
{
printf("WSASocket() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else
printf("WSASocket() is OK!\n");
}
memset(sndbuf, '$', MAX_BUFFER_SZ);
for(i=0; i < count ;i++)
{
wbuf.buf = sndbuf;
wbuf.len = MAX_BUFFER_SZ -1;
ret = WSASendTo(socks[0], &wbuf, 1, &dwBytesSent, 0, server, len , NULL, NULL);
if (ret == SOCKET_ERROR)
{
printf("WSASendTo() failed with error code %d\n", WSAGetLastError());
closesocket(socks[0]);
return FALSE;
}
else
printf("WSASendTo() looks OK!\n");
}
closesocket(socks[0]);
return TRUE;
}
// Function: ServerRecv
// Description: Read data pending on the given socket.
BOOL ServerRecv(int index)
{
WSABUF wbuf;
char rcvbuf[MAX_BUFFER_SZ];
SOCKADDR from;
DWORD dwBytesRecv, dwFlags, dwFromLen;
int ret;
wbuf.buf = rcvbuf;
dwFlags = 0;
dwFromLen = sizeof(from);
wbuf.len = MAX_BUFFER_SZ-1;
ret = WSARecvFrom(socks[index], &wbuf, 1, &dwBytesRecv, &dwFlags, &from, &dwFromLen, NULL, NULL);
if (ret == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
return TRUE;
else
{
printf("WSARecvFrom() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
}
else
printf("WSARecvFrom() should be fine!\n");
rcvbuf[dwBytesRecv] = 0;
printf("Read: [%s]\n", rcvbuf);
return TRUE;
}
// Function: FormatIPXAddr
// Description:
// Print an IPX address to stdout. We don't use the function
// WSAAddressToString because it doesn't support IPX.
char *FormatIPXAddr(SOCKADDR_IPX *addr)
{
static char dest[128];
wsprintf((LPWSTR)dest, L"%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X:%04X",
(unsigned char)addr->sa_netnum[0],
(unsigned char)addr->sa_netnum[1],
(unsigned char)addr->sa_netnum[2],
(unsigned char)addr->sa_netnum[3],
(unsigned char)addr->sa_nodenum[0],
(unsigned char)addr->sa_nodenum[1],
(unsigned char)addr->sa_nodenum[2],
(unsigned char)addr->sa_nodenum[3],
(unsigned char)addr->sa_nodenum[4],
(unsigned char)addr->sa_nodenum[5],
ntohs(addr->sa_socket));
return dest;
}
// Function: main
// Description:
// Initialize Winsock, parse the arguments, and start either the
// client or server depending on the arguments.
int main(int argc, char **argv)
{
WSANAMESPACE_INFO *nsinfo=NULL;
WSADATA wsd;
GUID svcguid;
int nscount, ret, i;
WCHAR szTemp[256];
if(argc < 2)
{
usage(argv[0]);
exit(1);
}
ValidateArgs(argc, argv);
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
printf("WSAStartup() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("WSAStartup() is pretty fine!\n");
// Create the GUID that everyone will use, client and server
SET_NETWARE_SVCID(&svcguid, dwSapId);
StringFromGUID2(&svcguid, szTemp, 256);
printf("GUID: [%S]\n", szTemp);
// Enumerate the name spaces that we can use
nsinfo = EnumNameSpaceProviders(&nscount);
if (!nsinfo)
{
printf("unable to enumerate name space info!\n");
return -1;
}
for(i=0; i < nscount ;i++)
printf("Found NS: %S\n", nsinfo[i].lpszIdentifier);
// If server
if (bServer)
{
if (szServerName[0] == '*')
{
printf("You must specify a server name!\n");
usage(argv[0]);
return -1;
}
else
printf("Server name provided!\n");
// Install the service class
if (!InstallServiceClass(&svcguid, nsinfo, nscount))
{
printf("Unable to install service class!\n");
return -1;
}
else
printf("Service class installed successfully!\n");
// Advertise our service
if (!Advertise(&svcguid, nsinfo, nscount, szServerName))
{
printf("Unable to advertise service!\n");
return -1;
}
else
printf("Service advertised successfully!\n");
// Register our services for the read event
for(i=0; i < nscount ;i++)
{
ret = WSAEventSelect(socks[i], hEvents[i], FD_READ);
if (ret == SOCKET_ERROR)
{
printf("WSAEventSelect() failed with error code: %d\n", WSAGetLastError());
return -1;
}
else
printf("WSAEventSelect() is OK!\n");
}
while (1)
{
// Read any incoming data
ret = WSAWaitForMultipleEvents(nscount, hEvents, FALSE, INFINITE, TRUE);
ServerRecv(ret - WSA_WAIT_EVENT_0);
}
}
else // If client
{
// Lookup the service
LookupService(&svcguid, dwSapId, dwNameSpace, szServerName);
}
HeapFree(GetProcessHeap(), 0, nsinfo);
WSACleanup();
return 0;
}
Build and run the project.
----------------------------------------------------------
< Register and install services | RnR Main | Query A Service & Form A Query >