home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / netds / winsock / rnr / rnrsrv / rnrsrv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-18  |  20.8 KB  |  752 lines

  1. /*++
  2.  
  3. Copyright (c) 1994 Microsoft Corporation
  4.  
  5. Module Name:
  6.  
  7.     RnrSrv.c
  8.  
  9. Abstract:
  10.  
  11.     Test and demonstration service for the RNR (service Registration and
  12.     Name Resolution) APIs.  This is a simple service designed to show
  13.     the basic principles involved in using the RNR APIs to _write a
  14.     protocol-independent Windows Sockets service.
  15.  
  16.     This service opens a number of listening sockets, waits for an
  17.     incoming connection from a client, accepts the connection, then
  18.     echos data back to the client until the client terminates the
  19.     virtual circuit.  This service is single-threaded and can handle
  20.     only a single client at a time.
  21.  
  22.     The OpenListeners() routine implemented herein is intended to be a
  23.     demonstration of RNR functionality commonly used in
  24.     protocol-independent services.  Service writers are encouraged to
  25.     leverage this code to assist them in writing protocol-independent
  26.     services on top of the Windows Sockets API.
  27.  
  28. --*/
  29.  
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <windows.h>
  33. #include <winsock2.h>
  34. #include <nspapi.h>
  35.  
  36. WSADATA WsaData;
  37.  
  38. PSTR ServiceTypeName = "EchoExample";
  39. PSTR ServiceName = "EchoServer";
  40.  
  41. #define MAX_SOCKETS    20
  42.  
  43. INT
  44. OpenListeners (
  45.     IN PTSTR ServiceName,
  46.     IN LPGUID ServiceType,
  47.     IN BOOL Reliable,
  48.     IN BOOL MessageOriented,
  49.     IN BOOL StreamOriented,
  50.     IN BOOL Connectionless,
  51.     OUT SOCKET SocketHandles[],
  52.     OUT INT ProtocolsUsed[]
  53.     );
  54.  
  55. INT
  56. AdvertiseService(
  57.     IN PTSTR ServiceName,
  58.     IN LPGUID ServiceType,
  59.     IN SOCKET SocketHandles[],
  60.     IN INT SocketCount
  61.     );
  62.  
  63.  
  64. void _CRTAPI1
  65. main (
  66.     int argc,
  67.     char *argv[]
  68.     )
  69. {
  70.     INT count, err, i ;
  71.     DWORD tmpProtocol[2];
  72.     BYTE buffer[1024];
  73.     DWORD bytesRequired;
  74.     PPROTOCOL_INFO protocolInfo;
  75.     GUID serviceType;
  76.     FD_SET readfds;
  77.     SOCKET listenSockets[MAX_SOCKETS+1];
  78.     INT protocols[MAX_SOCKETS+1];
  79.     SOCKET s;
  80.  
  81.     //
  82.     // Initialize the Windows Sockets DLL.
  83.     //
  84.  
  85.     err = WSAStartup( 0x0202, &WsaData );
  86.  
  87.     if ( err == SOCKET_ERROR )
  88.     {
  89.         printf( "WSAStartup() failed: %ld\n", GetLastError( ) );
  90.         return;
  91.     }
  92.  
  93.     //
  94.     // Determine the value of our GUID.  The GUID uniquely identifies
  95.     // the type of service we provide.
  96.     //
  97.  
  98.     err = GetTypeByName( ServiceTypeName, &serviceType );
  99.  
  100.     if ( err == SOCKET_ERROR )
  101.     {
  102.         printf( "GetTypeByName for \"%s\" failed: %ld\n",
  103.                     ServiceTypeName, GetLastError( ) );
  104.         exit( 1 );
  105.     }
  106.  
  107.     //
  108.     // Open listening sockets for this service.
  109.     //
  110.  
  111.     count = OpenListeners(
  112.                 ServiceName,
  113.                 &serviceType,
  114.                 TRUE,
  115.                 FALSE,
  116.                 FALSE,
  117.                 FALSE,
  118.                 listenSockets,
  119.                 protocols
  120.                 );
  121.  
  122.     if ( count <= 0 )
  123.     {
  124.         printf( "failed to open listenSockets for name \"%s\" type \"%s\"\n",
  125.                     ServiceName, ServiceTypeName );
  126.         exit( 1 );
  127.     }
  128.  
  129.     //
  130.     // We successfully opened some listening sockets.  Display some
  131.     // information on each protocol in use.
  132.     //
  133.  
  134.     tmpProtocol[1] = 0;
  135.  
  136.     for ( i = 0; i < count; i++ )
  137.     {
  138.         tmpProtocol[0] = protocols[i];
  139.  
  140.         bytesRequired = sizeof(buffer);
  141.         err = EnumProtocols( tmpProtocol, buffer, &bytesRequired );
  142.  
  143.         if ( err < 1 )
  144.         {
  145.             printf( "EnumProtocols failed for protocol %ld: %ld\n",
  146.                         tmpProtocol[0], GetLastError( ) );
  147.             exit( 1 );
  148.         }
  149.  
  150.         protocolInfo = (PPROTOCOL_INFO)buffer;
  151.         printf( "Socket %lx listening on protocol \"%s\" (%ld)\n",
  152.                     listenSockets[i],
  153.                     protocolInfo->lpProtocol,
  154.                     protocolInfo->iProtocol );
  155.  
  156.     }
  157.  
  158.     //
  159.     // Advertise the service so thet it can be found.
  160.     //
  161.     printf( "Going to advertise the service.\n" ) ;
  162.  
  163.     err = AdvertiseService(
  164.                 ServiceName,
  165.                 &serviceType,
  166.                 listenSockets,
  167.                 count) ;
  168.  
  169.     if (err == SOCKET_ERROR)
  170.     {
  171.         printf( "Failed to advertise the service. Error %d\n", GetLastError()) ;
  172.         exit( 1 ) ;
  173.     }
  174.  
  175.     printf( "Successfully advertised the service.\n" ) ;
  176.  
  177.     //
  178.     // Loop accepting connections and servicing them.
  179.     //
  180.  
  181.     FD_ZERO( &readfds );
  182.  
  183.     while ( TRUE )
  184.     {
  185.         //
  186.         // Add the listening sockets to the FD_SET we'll pass to select.
  187.         //
  188.  
  189.         for ( i = 0; i < count; i++ )
  190.         {
  191.             FD_SET( listenSockets[i], &readfds );
  192.         }
  193.  
  194.         //
  195.         // Wait for one of the listenSockets to receive an incoming connection.
  196.         //
  197.  
  198.         err = select( count, &readfds, NULL, NULL, NULL );
  199.  
  200.         if ( err < 1 )
  201.         {
  202.             printf( "select() returned %ld, error %ld\n", err, GetLastError( ) );
  203.             exit( 1 );
  204.         }
  205.  
  206.         //
  207.         // Find the socket that received an incoming connection and accept
  208.         // the connection.
  209.         //
  210.  
  211.         for ( i = 0; i < count; i++ )
  212.         {
  213.             if ( FD_ISSET( listenSockets[i], &readfds ) )
  214.                 break;
  215.         }
  216.  
  217.         //
  218.         // Accept the connection from the client.  We ignore the client's
  219.         // address here.
  220.         //
  221.  
  222.         s = accept( listenSockets[i], NULL, NULL );
  223.  
  224.         if ( s == INVALID_SOCKET )
  225.         {
  226.             printf( "accept() failed, error %ld\n", GetLastError( ) );
  227.             exit( 1 );
  228.         }
  229.  
  230.         printf( "Accepted incoming connection on socket %lx\n",
  231.                 listenSockets[i] );
  232.  
  233.         //
  234.         // Loop echoing data back to the client.  Note that this
  235.         // single-threaded service can handle only a single client at a
  236.         // time.  A more sophisticated service would service multiple
  237.         // clients simultaneously by using multiple threads or
  238.         // asynchronous I/O.
  239.         //
  240.  
  241.         while ( TRUE )
  242.         {
  243.             err = recv( s, buffer, sizeof(buffer), 0 );
  244.             if ( err == 0 )
  245.             {
  246.                 printf( "Connection terminated gracefully.\n" );
  247.                 break;
  248.             }
  249.             else if ( err < 0 )
  250.             {
  251.                 err = GetLastError();
  252.  
  253.                 if ( err == WSAEDISCON )
  254.                 {
  255.                     printf( "Connection disconnected.\n" );
  256.                 }
  257.                 else
  258.                 {
  259.                     printf( "recv() failed, error %ld.\n", err );
  260.                 }
  261.  
  262.                 break;
  263.             }
  264.  
  265.             err = send( s, buffer, err, 0 );
  266.  
  267.             if ( err < 0 )
  268.             {
  269.                 printf( "send() failed, error %ld\n", GetLastError( ) );
  270.                 break;
  271.             }
  272.         }
  273.  
  274.         //
  275.         // Close the connected socket and continue accepting connections.
  276.         //
  277.  
  278.         closesocket( s );
  279.     }
  280.  
  281. } // main
  282.  
  283.  
  284.  
  285. INT
  286. OpenListeners (
  287.     IN PTSTR ServiceName,
  288.     IN LPGUID ServiceType,
  289.     IN BOOL Reliable,
  290.     IN BOOL MessageOriented,
  291.     IN BOOL StreamOriented,
  292.     IN BOOL Connectionless,
  293.     OUT SOCKET SocketHandles[],
  294.     OUT INT ProtocolsUsed[]
  295.     )
  296.  
  297. /*++
  298.  
  299. Routine Description:
  300.  
  301.     Examines the Windows Sockets transport protocols loaded on a machine
  302.     and opens listening sockets on all the protocols which support the
  303.     characteristics requested by the caller.
  304.  
  305. Arguments:
  306.  
  307.     ServiceName - a friendly name which identifies this service.  On
  308.         name spaces which support name resolution at the service level
  309.         (e.g.  SAP) this is the name clients will use to connect to this
  310.         service.  On name spaces which support name resolution at the
  311.         host level (e.g.  DNS) this name is ignored and applications
  312.         must use the host name to establish communication with this
  313.         service.
  314.  
  315.     ServiceType - the GUID value which uniquely identifies the type of
  316.         service we provide.  A GUID is created with the UUIDGEN program.
  317.  
  318.     Reliable - if TRUE, the caller requests that only transport protocols
  319.         which support reliable data delivery be used.  If FALSE, both
  320.         reliable and unreliable protocols may be used.
  321.  
  322.     MessageOriented - if TRUE, only message-oriented transport protocols
  323.         should be used.  If FALSE, the caller either does not care
  324.         whether the protocols used are message oriented or desires only
  325.         stream-oriented protocols.
  326.  
  327.     StreamOriented - if TRUE, only stream-oriented transport protocols
  328.         should be used.  If FALSE, the caller either does not care
  329.         whether the protocols used are stream oriented or desires only
  330.         message-oriented protocols.
  331.  
  332.     Connectionless - if TRUE, only connectionless protocols should be
  333.         used.  If FALSE, both connection-oriented and connectionless
  334.         protocols may be used.
  335.  
  336.     SocketHandles - an array of size MAX_SOCKETS which receives listening
  337.         socket handles.
  338.  
  339.     ProtocolsUsed - an array of size MAX_SOCKETS which receives the
  340.         protocol values for each of the socket handles in the
  341.         SocketHandles array.
  342.  
  343. Return Value:
  344.  
  345.     The count of listening sockets successfully opened, or -1 if no
  346.     sockets could be successfully opened that met the desired
  347.     characteristics.
  348.  
  349. --*/
  350.  
  351. {
  352.     INT            protocols[MAX_SOCKETS+1];
  353.     BYTE           buffer[2048];
  354.     DWORD          bytesRequired;
  355.     INT            err;
  356.     PPROTOCOL_INFO protocolInfo;
  357.     PCSADDR_INFO   csaddrInfo;
  358.     INT            protocolCount;
  359.     INT            addressCount;
  360.     INT            i;
  361.     DWORD          protocolIndex;
  362.     SOCKET         s;
  363.     DWORD          index = 0;
  364.  
  365.     //
  366.     // First look up the protocols installed on this machine.  The
  367.     // EnumProtocols() API returns about all the Windows Sockets
  368.     // protocols loaded on this machine, and we'll use this information
  369.     // to identify the protocols which provide the necessary semantics.
  370.     //
  371.  
  372.     bytesRequired = sizeof(buffer);
  373.  
  374.     err = EnumProtocols( NULL, buffer, &bytesRequired );
  375.  
  376.     if ( err <= 0 )
  377.     {
  378.         return 0;
  379.     }
  380.  
  381.     //
  382.     // Walk through the available protocols and pick out the ones which
  383.     // support the desired characteristics.
  384.     //
  385.  
  386.     protocolCount = err;
  387.     protocolInfo = (PPROTOCOL_INFO)buffer;
  388.  
  389.     for ( i = 0, protocolIndex = 0;
  390.           i < protocolCount && protocolIndex < MAX_SOCKETS;
  391.           i++, protocolInfo++ )
  392.     {
  393.         //
  394.         // If "reliable" support is requested, then check if supported
  395.         // by this protocol.  Reliable support means that the protocol
  396.         // guarantees delivery of data in the order in which it is sent.
  397.         // Note that we assume here that if the caller requested reliable
  398.         // service then they do not want a connectionless protocol.
  399.         //
  400.  
  401.         if ( Reliable )
  402.         {
  403.             //
  404.             // Check to see if the protocol is reliable.  It must
  405.             // guarantee both delivery of all data and the order in
  406.             // which the data arrives.  Also, it must not be a
  407.             // connectionless protocol.
  408.             //
  409.  
  410.             if ( (protocolInfo->dwServiceFlags &
  411.                       XP_GUARANTEED_DELIVERY) == 0 ||
  412.                  (protocolInfo->dwServiceFlags &
  413.                       XP_GUARANTEED_ORDER) == 0 )
  414.             {
  415.                 continue;
  416.             }
  417.  
  418.             if ( (protocolInfo->dwServiceFlags & XP_CONNECTIONLESS) != 0 )
  419.             {
  420.                 continue;
  421.             }
  422.  
  423.             //
  424.             // Check to see that the protocol matches the stream/message
  425.             // characteristics requested.  A stream oriented protocol
  426.             // either has the XP_MESSAGE_ORIENTED bit turned off, or
  427.             // else supports "pseudo stream" capability.  Pseudo stream
  428.             // means that although the underlying protocol is message
  429.             // oriented, the application may open a socket of type
  430.             // SOCK_STREAM and the protocol will hide message boundaries
  431.             // from the application.
  432.             //
  433.  
  434.             if ( StreamOriented &&
  435.                  (protocolInfo->dwServiceFlags & XP_MESSAGE_ORIENTED) != 0 &&
  436.                  (protocolInfo->dwServiceFlags & XP_PSEUDO_STREAM) == 0 )
  437.             {
  438.                 continue;
  439.             }
  440.  
  441.             if ( MessageOriented &&
  442.                  (protocolInfo->dwServiceFlags & XP_MESSAGE_ORIENTED) == 0 )
  443.             {
  444.                 continue;
  445.             }
  446.  
  447.         }
  448.         else if ( Connectionless )
  449.         {
  450.             //
  451.             // Make sure that this is a connectionless protocol.  In a
  452.             // connectionless protocol, data is sent as discrete
  453.             // datagrams with no connection establishment required.
  454.             // Connectionless protocols typically have no reliability
  455.             // guarantees.
  456.             //
  457.  
  458.             if ( (protocolInfo->dwServiceFlags & XP_CONNECTIONLESS) != 0 )
  459.             {
  460.                 continue;
  461.             }
  462.         }
  463.  
  464.         //
  465.         // This protocol fits all the criteria.  Add it to the list of
  466.         // protocols in which we're interested.
  467.         //
  468.  
  469.         protocols[protocolIndex++] = protocolInfo->iProtocol;
  470.     }
  471.  
  472.     //
  473.     // Make sure that we found at least one acceptable protocol.  If
  474.     // there no protocols on this machine which meet the caller's
  475.     // requirements then fail here.
  476.     //
  477.  
  478.     if ( protocolIndex == 0 )
  479.     {
  480.         return 0;
  481.     }
  482.  
  483.     protocols[protocolIndex] = 0;
  484.  
  485.     //
  486.     // Now attempt to find the socket addresses to which we need to
  487.     // bind.  Note that we restrict the scope of the search to those
  488.     // protocols of interest by passing the protocol array we generated
  489.     // above to GetAddressByName().  This forces GetAddressByName() to
  490.     // return socket addresses for only the protocols we specify,
  491.     // ignoring possible addresses for protocols we cannot support
  492.     // because of the caller's constraints.
  493.     //
  494.  
  495.     bytesRequired = sizeof(buffer);
  496.  
  497.     err = GetAddressByName(
  498.                NS_DEFAULT,
  499.                ServiceType,
  500.                ServiceName,
  501.                protocols,
  502.                RES_SERVICE | RES_FIND_MULTIPLE,
  503.                NULL,                     // lpServiceAsyncInfo
  504.                buffer,
  505.                &bytesRequired,
  506.                NULL,                     // lpAliasBuffer
  507.                NULL                      // lpdwAliasBufferLength
  508.                );
  509.  
  510.     if ( err <= 0 )
  511.     {
  512.         return 0;
  513.     }
  514.  
  515.     //
  516.     // For each address, open a socket and attempt to listen. Note
  517.     // that if anything fails for a particular protocol we just skip on
  518.     // to the next protocol. As long as we can successfully listen on
  519.     // one protocol we are satisfied here.
  520.     //
  521.  
  522.     addressCount = err;
  523.     csaddrInfo = (PCSADDR_INFO)buffer;
  524.  
  525.     for ( i = 0; i < addressCount; i++, csaddrInfo++ )
  526.     {
  527.         //
  528.         // Open the socket. Note that we manually specify stream type
  529.         // if so requested in case the protocol is natively a message
  530.         // protocol but supports stream semantics.
  531.         //
  532.  
  533.         s = socket( csaddrInfo->LocalAddr.lpSockaddr->sa_family,
  534.                     StreamOriented ? SOCK_STREAM : csaddrInfo->iSocketType,
  535.                     csaddrInfo->iProtocol );
  536.  
  537.         if ( s == INVALID_SOCKET )
  538.         {
  539.             continue;
  540.         }
  541.  
  542.         //
  543.         // Bind the socket to the local address specified.
  544.         //
  545.  
  546.         err = bind( s, csaddrInfo->LocalAddr.lpSockaddr,
  547.                     csaddrInfo->LocalAddr.iSockaddrLength );
  548.  
  549.         if ( err != NO_ERROR )
  550.         {
  551.             closesocket( s );
  552.             continue;
  553.         }
  554.  
  555.         //
  556.         // Start listening for incoming sockets on the socket if this is
  557.         // not a datagram socket.  If this is a datagram socket, then
  558.         // the listen() API doesn't make sense; doing a bind() is
  559.         // sufficient to listen for incoming datagrams on a
  560.         // connectionless protocol.
  561.         //
  562.  
  563.         if ( csaddrInfo->iSocketType != SOCK_DGRAM )
  564.         {
  565.             err = listen( s, 5 );
  566.  
  567.             if ( err != NO_ERROR )
  568.             {
  569.                 closesocket( s );
  570.                 continue;
  571.             }
  572.         }
  573.  
  574.         //
  575.         // The socket was successfully opened and we're listening on it.
  576.         // Remember the protocol used and the socket handle and continue
  577.         // listening on other protocols.
  578.         //
  579.  
  580.         ProtocolsUsed[index] = csaddrInfo->iProtocol;
  581.         SocketHandles[index] = s;
  582.  
  583.         index++;
  584.         if ( index == MAX_SOCKETS )
  585.         {
  586.             return index;
  587.         }
  588.     }
  589.  
  590.     (void) LocalFree( (HLOCAL) csaddrInfo );
  591.  
  592.     //
  593.     // Return the count of sockets that we're sucecssfully listening on.
  594.     //
  595.  
  596.     return index;
  597.  
  598. } // OpenListeners
  599.  
  600.  
  601. INT
  602. AdvertiseService(
  603.     IN PTSTR ServiceName,
  604.     IN LPGUID ServiceType,
  605.     IN SOCKET SocketHandles[],
  606.     IN INT SocketCount
  607.     )
  608. /*++
  609.  
  610. Routine Description:
  611.  
  612.     Advertises this service on all the default name spaces.
  613.  
  614. Arguments:
  615.  
  616.     ServiceName - the name of the service.
  617.  
  618.     ServiceType - the GUID value which uniquely the service.
  619.  
  620.     SocketHandles - array of sockets that we have opened. For each socket,
  621.         we do a getsockname() to discover the actual local address.
  622.  
  623.     SocketCount - number of sockets in SockHandles[]
  624.  
  625. Return Value:
  626.  
  627.     0 if success. SOCK_ERROR otherwise.
  628.  
  629. --*/
  630.  
  631. {
  632.  
  633.     WSAVERSION          Version;
  634.     WSAQUERYSET         QuerySet;
  635.     LPCSADDR_INFO       lpCSAddrInfo;
  636.     PSOCKADDR           sockAddr ;
  637.     BYTE *              addressBuffer;
  638.     DWORD               addressBufferSize ;
  639.     DWORD               successCount = 0 ;
  640.     INT                 i, err ;
  641.  
  642.     //
  643.     // Allocate some memory for the CSADDR_INFO structures.
  644.     //
  645.  
  646.     lpCSAddrInfo = (LPCSADDR_INFO) malloc( sizeof(CSADDR_INFO) * SocketCount );
  647.  
  648.     if (!lpCSAddrInfo)
  649.     {
  650.         SetLastError(ERROR_NOT_ENOUGH_MEMORY) ;
  651.         return SOCKET_ERROR ;
  652.     }
  653.  
  654.     //
  655.     // Allocate some memory for the SOCKADDR addresses returned
  656.     // by getsockname().
  657.     //
  658.  
  659.     addressBufferSize = SocketCount * sizeof(SOCKADDR);
  660.     addressBuffer = malloc( addressBufferSize ) ;
  661.  
  662.     if (!addressBuffer)
  663.     {
  664.         free(lpCSAddrInfo) ;
  665.         SetLastError(ERROR_NOT_ENOUGH_MEMORY) ;
  666.         return SOCKET_ERROR ;
  667.     }
  668.  
  669.     RtlZeroMemory( &QuerySet, sizeof( WSAQUERYSET ) );
  670.  
  671.     //
  672.     // For each socket, get its local association.
  673.     //
  674.  
  675.     sockAddr = (PSOCKADDR) addressBuffer ;
  676.  
  677.     for (i = 0; i < SocketCount; i++)
  678.     {
  679.         int size = (int) addressBufferSize ;
  680.  
  681.         //
  682.         // Call getsockname() to get the local association for the socket.
  683.         //
  684.  
  685.         err = getsockname(
  686.                   SocketHandles[i],
  687.                   sockAddr,
  688.                   &size) ;
  689.  
  690.         if (err == SOCKET_ERROR)
  691.         {
  692.             continue ;
  693.         }
  694.  
  695.         //
  696.         // Now setup the Addressing information for this socket.
  697.         // Only the dwAddressType, dwAddressLength and lpAddress
  698.         // is of any interest in this example.
  699.         //
  700.  
  701.         lpCSAddrInfo[i].LocalAddr.iSockaddrLength = size;
  702.         lpCSAddrInfo[i].LocalAddr.lpSockaddr = sockAddr;
  703.         lpCSAddrInfo[i].RemoteAddr.iSockaddrLength = size;
  704.         lpCSAddrInfo[i].RemoteAddr.lpSockaddr = sockAddr;
  705.         lpCSAddrInfo[i].iSocketType = SOCK_RDM; // Reliable
  706.         lpCSAddrInfo[i].iProtocol = sockAddr->sa_family;
  707.  
  708.         //
  709.         // Advance pointer and adjust buffer size. Assumes that
  710.         // the structures are aligned.
  711.         //
  712.  
  713.         addressBufferSize -= size ;
  714.         sockAddr = (PSOCKADDR) ((BYTE*)sockAddr + size)  ;
  715.  
  716.         successCount++ ;
  717.     }
  718.  
  719.     //
  720.     // If we got at least one address, go ahead and advertise it.
  721.     //
  722.  
  723.     if (successCount)
  724.     {
  725.         QuerySet.dwSize = sizeof( WSAQUERYSET );
  726.         QuerySet.lpServiceClassId = ServiceType;
  727.         QuerySet.lpszServiceInstanceName = ServiceName;
  728.         QuerySet.lpszComment = "D/C/M's Example Echo Service";
  729.         QuerySet.lpVersion = &Version;
  730.         QuerySet.lpVersion->dwVersion = 1;
  731.         QuerySet.lpVersion->ecHow = COMP_NOTLESS;
  732.         QuerySet.dwNameSpace = NS_ALL;
  733.         QuerySet.dwNumberOfCsAddrs = successCount;
  734.         QuerySet.lpcsaBuffer = lpCSAddrInfo;
  735.  
  736.         err = WSASetService( &QuerySet,
  737.                              RNRSERVICE_REGISTER,
  738.                              SERVICE_MULTIPLE );
  739.  
  740.         if ( err )
  741.             err = SOCKET_ERROR;
  742.     }
  743.     else
  744.         err = SOCKET_ERROR ;
  745.  
  746.     free (addressBuffer) ;
  747.  
  748.     return (err) ;
  749. }
  750.  
  751.  
  752.