/*++

Copyright (c) 1994  Microsoft Corporation

Module Name:

    gtsadrnr.c

Abstract:

    This module contains the code to support the new RnR APIs. Some
    support code is in getaddr.c, but the interface routines and
    routines specific to RnR2 are herein.

Author:

    ArnoldM      4-Dec-1995
Revision History:

    ArnoldM      Created

--*/


#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include "ncp.h"
#include <rpc.h>
#include <winsock2.h>
#include <ws2spi.h>
#include <wsipx.h>
#include <rnrdefs.h>
#include <svcguid.h>
#include <rnraddrs.h>


//-------------------------------------------------------------------//
//                                                                   //
// Globals in getaddr.c and provider .c                              //
//                                                                   //
//-------------------------------------------------------------------//

GUID HostAddrByInetStringGuid = SVCID_INET_HOSTADDRBYINETSTRING;
GUID ServiceByNameGuid = SVCID_INET_SERVICEBYNAME;
GUID HostAddrByNameGuid = SVCID_INET_HOSTADDRBYNAME;
GUID HostNameGuid = SVCID_HOSTNAME;

DWORD
NwpGetAddressByName(
    IN    LPWSTR  Reserved,
    IN    WORD    nServiceType,
    IN    LPWSTR  lpServiceName,
    IN OUT LPSOCKADDR_IPX  lpsockaddr
);

DWORD
NwpGetAddressViaSap(
    IN WORD        nServiceType,
    IN LPWSTR      lpServiceName,
    IN DWORD       nProt,
    IN OUT LPVOID  lpCsAddrBuffer,
    IN OUT LPDWORD lpdwBufferLength,
    IN HANDLE      hCancellationEvent,
    OUT LPDWORD    lpcAddress
);

DWORD
NwpRnR2AddServiceType(
    IN  LPWSTR   lpServiceTypeName,
    IN  LPGUID   lpClassType,
    IN  WORD     wSapId,
    IN  WORD     wPort
);

BOOL
NwpRnR2RemoveServiceType(
    IN  LPGUID   lpServiceType
);

DWORD
SapFreeSapSocket(
    SOCKET s
    );

PSAP_RNR_CONTEXT
SapMakeContext
    (
       IN HANDLE Handle,
       DWORD     dwExcess
    );

BOOL
NwpLookupSapInRegistry(
    IN  LPGUID    lpServiceType,
    OUT PWORD     pnSapType,
    OUT PWORD     pwPort,
    IN OUT PDWORD pfConnectionOriented
);

PSAP_RNR_CONTEXT
SapGetContext(
    HANDLE h
    );

DWORD
DoASap(
    IN PSAP_RNR_CONTEXT psrcContext,
    IN WORD QueryType,
    IN PBYTE * ppData,
    IN LPWSAQUERYSETW lpqsResults,
    IN PLONG   pl,
    IN PDWORD  pdw
    );

VOID
SapReleaseContext(
    IN PSAP_RNR_CONTEXT psrcContext
    );

DWORD
SapGetSapSocket(
    SOCKET * ps
    );

DWORD
PrepareForSap(
    IN PSAP_RNR_CONTEXT psrc
    );

DWORD
SapGetSapForType(
    PSAP_BCAST_CONTROL psbc,
    WORD               nServiceType
    );

DWORD
FillBufferWithCsAddr(
    IN LPBYTE       pAddress,
    IN DWORD        nProt,
    IN OUT LPVOID   lpCsAddrBuffer,
    IN OUT LPDWORD  lpdwBufferLength,
    OUT LPDWORD     pcAddress
    );

DWORD
pSapSetService2(
    IN DWORD dwOperation,
    IN LPWSTR lpszServiceInstanceName,
    IN PBYTE  pbAddress,
    IN LPGUID pServiceType,
    IN WORD nServiceType
    );

DWORD
NwpGetRnRAddress(
         PHANDLE phServer,
         PWCHAR  pwszContext,
         PLONG   plIndex,
         LPWSTR  lpServiceName,
         WORD    nServiceType,
         PDWORD  pdwVersion,
         DWORD   dwInSize,
         LPWSTR  OutName,
         SOCKADDR_IPX * pSockAddr
    );


DWORD
NwpSetClassInfo(
    IN    LPWSTR   lpwszClassName,
    IN    LPGUID   lpClassId,
    IN    PCHAR    lpbProp
    );

VOID
pFreeAllContexts();

extern DWORD oldRnRServiceRegister, oldRnRServiceDeRegister;
//-------------------------------------------------------------------//
//                                                                   //
// Local Function Prototypes                                         //
//                                                                   //
//-------------------------------------------------------------------//

INT WINAPI
pGetServiceClassInfo(
    IN  LPGUID               lpProviderId,
    IN OUT LPDWORD    lpdwBufSize,
    IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo,
    IN  PBOOL          pfAdvert
);

DWORD
NwpSetClassInfoAdvertise(
       IN   LPGUID   lpClassType,
       IN   WORD     wSapId
);

BOOL
NSPpCheckCancel(
    PVOID pvArg
    );

DWORD
NSPpGotSap(
    PSAP_BCAST_CONTROL psbc,
    PSAP_IDENT_HEADER pSap,
    PDWORD pdwErr
    );

INT WINAPI
NSPLookupServiceBegin(
    IN  LPGUID               lpProviderId,
    IN  LPWSAQUERYSETW       lpqsRestrictions,
    IN  LPWSASERVICECLASSINFOW  lpServiceClassInfo,
    IN  DWORD                dwControlFlags,
    OUT LPHANDLE             lphLookup
    );

INT WINAPI
NSPLookupServiceNext(
    IN     HANDLE          hLookup,
    IN     DWORD           dwControlFlags,
    IN OUT LPDWORD         lpdwBufferLength,
    OUT    LPWSAQUERYSETW  lpqsResults
    );

INT WINAPI
NSPUnInstallNameSpace(
    IN LPGUID lpProviderId
    );

INT WINAPI
NSPCleanup(
    IN LPGUID lpProviderId
    );

INT WINAPI
NSPLookupServiceEnd(
    IN HANDLE hLookup
    );

INT WINAPI
NSPSetService(
    IN  LPGUID               lpProviderId,
    IN  LPWSASERVICECLASSINFOW  lpServiceClassInfo,
    IN LPWSAQUERYSETW lpqsRegInfo,
    IN WSAESETSERVICEOP essOperation,
    IN DWORD          dwControlFlags
    );

INT WINAPI
NSPInstallServiceClass(
    IN  LPGUID               lpProviderId,
    IN LPWSASERVICECLASSINFOW lpServiceClassInfo
    );

INT WINAPI
NSPRemoveServiceClass(
    IN  LPGUID               lpProviderId,
    IN LPGUID lpServiceCallId
    );

INT WINAPI
NSPGetServiceClassInfo(
    IN  LPGUID               lpProviderId,
    IN OUT LPDWORD    lpdwBufSize,
    IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo
    );
//-------------------------------------------------------------------//
//                                                                   //
// Data definitions                                                  //
//                                                                   //
//-------------------------------------------------------------------//

NSP_ROUTINE nsrVector = {
    FIELD_OFFSET(NSP_ROUTINE, NSPIoctl),
    1,                                    // major version
    1,                                    // minor version
    NSPCleanup,
    NSPLookupServiceBegin,
    NSPLookupServiceNext,
    NSPLookupServiceEnd,
    NSPSetService,
    NSPInstallServiceClass,
    NSPRemoveServiceClass,
    NSPGetServiceClassInfo
    };

static
GUID HostnameGuid = SVCID_HOSTNAME;

static
GUID InetHostname = SVCID_INET_HOSTADDRBYNAME;

static
GUID AddressGuid = SVCID_INET_HOSTADDRBYINETSTRING;

static
GUID IANAGuid = SVCID_INET_SERVICEBYNAME;

#define GuidEqual(x,y) RtlEqualMemory(x, y, sizeof(GUID))

    
//------------------------------------------------------------------//
//                                                                  //
//  Function Bodies                                                 //
//                                                                  //
//------------------------------------------------------------------//

DWORD
NwpSetClassInfoAdvertise(
       IN   LPGUID   lpClassType,
       IN   WORD     wSapId
)
{
/*++
Routine Description:
  Start a class info advertise. Called upon a SetService call
--*/
    PWCHAR pwszUuid;
    SOCKADDR_IPX sock;

    if (UuidToString(lpClassType, &pwszUuid) != RPC_S_OK) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return SOCKET_ERROR;
    }

    memset(&sock, 0, sizeof(sock));

    *(PWORD)&sock.sa_netnum = htons(wSapId);    // encode the ID here

    return(pSapSetService2( oldRnRServiceRegister,
                            pwszUuid,
                            (PBYTE)&sock,
                            lpClassType,
                            RNRCLASSSAPTYPE));
}

DWORD
pRnRReturnString(
    IN  PWCHAR   pwszSrc,
    IN OUT PBYTE *ppData,
    IN OUT PLONG plBytes
    )
{
/*++
Routine Description:
    Utility routine to pack a string into the spare buffer space,
    update pointers and counts. If the string fits, it is copied. If
    not, the pointer and count are still updated so the caller can
    compute the additional space needed
--*/

    PBYTE pSave = *ppData;
    LONG lLen = (wcslen(pwszSrc) + 1) * sizeof(WCHAR);

    *ppData = pSave + lLen;     // update the buffer pointer

    *plBytes = *plBytes - lLen;   // and the count

    if(*plBytes >= 0)
    {
        //
        // it fits.
        //

        RtlMoveMemory(pSave,
                      pwszSrc,
                      lLen);
        return(NO_ERROR);
    }
    return(WSAEFAULT);
}

DWORD
pLocateSapIdAndProtocls(
       IN LPGUID               lpClassInfoType,
       IN DWORD                dwNumClassInfo,
       IN LPWSANSCLASSINFOW    lpClassInfoBuf,
       OUT PWORD               pwSapId,
       OUT PDWORD              pdwnProt
    )
/*++
Routine Description:
    Locate the SAP ID entry and return it. Also return
    the protocols support.
--*/
{
    DWORD err = NO_ERROR;

    if(GuidEqual(lpClassInfoType, &HostnameGuid)
                 ||
       GuidEqual(lpClassInfoType, &InetHostname) )
    {
        *pwSapId = 0x4;
    }
    else if(IS_SVCID_NETWARE(lpClassInfoType))
    {
        *pwSapId = SAPID_FROM_SVCID_NETWARE(lpClassInfoType);
    }       
    else
    {
        for(; dwNumClassInfo; dwNumClassInfo--, lpClassInfoBuf++)
        {
            //
            // We have some class info data. Rummage through it
            // looking for what we want
            //

            if(!_wcsicmp(SERVICE_TYPE_VALUE_SAPIDW, lpClassInfoBuf->lpszName)
                           &&
               (lpClassInfoBuf->dwValueType == REG_DWORD)
                           &&
               (lpClassInfoBuf->dwValueSize >= sizeof(WORD)))
            {
                //
                // got it
                //

                *pwSapId = *(PWORD)lpClassInfoBuf->lpValue;
                break;

            }
        }
        if(!dwNumClassInfo)
        {
            err = WSA_INVALID_PARAMETER;
        }
    }
    *pdwnProt = SPX_BIT | SPXII_BIT | IPX_BIT;
    return(err);
}
DWORD
pRnRReturnResults(
    IN    PWCHAR  pwszString,
    IN    LPGUID  pgdServiceClass,
    IN    DWORD   dwVersion,
    IN OUT PBYTE *ppData,
    IN OUT PLONG plBytes,
    IN PBYTE lpSockAddr,
    IN  DWORD     nProt,
    IN  DWORD     dwControlFlags,
    OUT    LPWSAQUERYSETW  lpqsResults
    )
{
/*++
Routine Description:
   Return the requested results
--*/
    DWORD err;

    lpqsResults->dwNameSpace = NS_SAP;

    if(dwControlFlags & LUP_RETURN_TYPE)
    {

        lpqsResults->lpServiceClassId = (LPGUID)*ppData;
        *ppData += sizeof(GUID);
        *plBytes -= sizeof(GUID);
        if(*plBytes >= 0)
        {
            *lpqsResults->lpServiceClassId = *pgdServiceClass;
        }
    }

    if(dwVersion
          &&
       (dwControlFlags & LUP_RETURN_VERSION) )
    {
        //
        // have a verion, and the caller wants it
        //

        lpqsResults->lpVersion = (LPWSAVERSION)*ppData;
        *ppData += sizeof(WSAVERSION);
        *plBytes -= sizeof(WSAVERSION);
        if(*plBytes >= 0)
        {
            //
            // and it fits. So return it
            //
            lpqsResults->lpVersion->dwVersion = dwVersion;
            lpqsResults->lpVersion->ecHow = COMP_EQUAL;
        }
    }
        
    if(dwControlFlags & LUP_RETURN_ADDR)
    {
        DWORD dwCsAddrLen;

        if(*plBytes >= 0)
        {
            dwCsAddrLen = (DWORD)*plBytes;       // all of it for now
        }
        else
        {
            dwCsAddrLen = 0;
        }
        lpqsResults->lpcsaBuffer = (PVOID)*ppData;
    
        err = FillBufferWithCsAddr(
                      lpSockAddr,
                      nProt,
                      (PVOID)lpqsResults->lpcsaBuffer,
                      &dwCsAddrLen,
                      &lpqsResults->dwNumberOfCsAddrs);

        //
        // see if it fit. Whether it did or not, compute the space available,
        // align, and do the rest
        //

    
        if(err == NO_ERROR)
        {
            //
            // if it worked, we have to compute the space taken
            //

            dwCsAddrLen = lpqsResults->dwNumberOfCsAddrs * (sizeof(CSADDR_INFO) +
                                       2*sizeof(SOCKADDR_IPX));
        }
        else if(err == ERROR_INSUFFICIENT_BUFFER)
        {
            err = WSAEFAULT;
        }

        *plBytes = *plBytes - dwCsAddrLen;

        *ppData = *ppData + dwCsAddrLen;
    }
    else
    {
        err = NO_ERROR;
    }

    //
    // no padding needed.
    
    if((dwControlFlags & LUP_RETURN_NAME))
    {
        lpqsResults->lpszServiceInstanceName = (LPWSTR)*ppData;
        err = pRnRReturnString(
                pwszString,
                ppData,
                plBytes);
    }
    if(pgdServiceClass)
    {
        //
        // Do we really return this?
        //
    }
    return(err);
}

INT WINAPI
NSPLookupServiceBegin(
    IN  LPGUID               lpProviderId,
    IN  LPWSAQUERYSETW       lpqsRestrictions,
    IN  LPWSASERVICECLASSINFOW  lpServiceClassInfo,
    IN  DWORD                dwControlFlags,
    OUT LPHANDLE             lphLookup
    )
/*++
Routine Description:
    This is the RnR routine that begins a lookup.
--*/
{
    PSAP_RNR_CONTEXT psrc;
    int err;
    DWORD nProt, nProt1;
    OEM_STRING OemServiceName;
    LPWSTR pwszContext;
    WORD wSapid;
    DWORD                dwNumClassInfo;
    LPWSANSCLASSINFOW    lpClassInfoBuf;

    //
    // Do parameter checking.
    //
    if ( lpqsRestrictions == NULL ||
         lpProviderId == NULL )
    {
        SetLastError( WSA_INVALID_PARAMETER );
        return( SOCKET_ERROR );
    }

    if ( lpqsRestrictions->dwNameSpace != NS_ALL &&
         lpqsRestrictions->dwNameSpace != NS_SAP )
    {
        SetLastError( WSAEINVAL );
        return( SOCKET_ERROR );
    }

    if ( lpqsRestrictions->lpServiceClassId == NULL )
    {
        SetLastError( WSA_INVALID_PARAMETER );
        return( SOCKET_ERROR );
    }

    //
    // Test to see if the ServiceClassId is TCP's, if so
    // we don't do the lookup.
    if ( lpqsRestrictions->lpServiceClassId &&
         ( GuidEqual( lpqsRestrictions->lpServiceClassId,
                       &HostAddrByInetStringGuid ) ||
           GuidEqual( lpqsRestrictions->lpServiceClassId,
                       &ServiceByNameGuid ) ||
           GuidEqual( lpqsRestrictions->lpServiceClassId,
                       &HostNameGuid ) ||
           GuidEqual( lpqsRestrictions->lpServiceClassId,
                       &HostAddrByNameGuid ) ) )
    {
        SetLastError( WSASERVICE_NOT_FOUND );
        return( SOCKET_ERROR );
    }

    if(lpqsRestrictions->dwSize < sizeof(WSAQUERYSETW))
    {
        SetLastError(WSAEFAULT);
        return(SOCKET_ERROR);
    }

    if(lpqsRestrictions->lpszContext
                  &&
       (lpqsRestrictions->lpszContext[0] != 0)
                  &&   
       wcscmp(&lpqsRestrictions->lpszContext[0],  L"\\") )
    {
        //
        // if not the default context, we must copy it.
        //
        pwszContext = lpqsRestrictions->lpszContext;
    }
    else
    {
        //
        // map all default contexts into "no context".
        //
        pwszContext = 0;
    }

    //
    // Compute protocols to return, or return them all
    //
    if(lpqsRestrictions->lpafpProtocols)
    {
        //
        // Make certain at least one IPX/SPX protocol is being requested
        //

        DWORD i;

        nProt = 0;

        for ( i = 0; i < lpqsRestrictions->dwNumberOfProtocols; i++ )
        {
            
            if((lpqsRestrictions->lpafpProtocols[i].iAddressFamily == AF_IPX)
                                        ||
               (lpqsRestrictions->lpafpProtocols[i].iAddressFamily == AF_UNSPEC)
              )
            {
                switch(lpqsRestrictions->lpafpProtocols[i].iProtocol) 
                {
                    case NSPROTO_IPX:
                        nProt |= IPX_BIT;
                        break;
                    case NSPROTO_SPX:
                        nProt |= SPX_BIT;
                        break;
                    case NSPROTO_SPXII:
                        nProt |= SPXII_BIT;
                        break;
                    default:
                        break;
                }
            }
        }

        if(!nProt)
        {
            //
            // if the caller doesn't want IPX/SPX addresses, why bother?
            //
            SetLastError(WSANO_DATA);
            return(SOCKET_ERROR);  
        }
    }
    else
    {
        nProt = IPX_BIT | SPX_BIT | SPXII_BIT;
    }

    if(dwControlFlags & LUP_CONTAINERS)
    {
        if(pwszContext)
        {
BadGuid:
           SetLastError(WSANO_DATA);
           return(SOCKET_ERROR);       // can't handle containers in containers
        }
        wSapid = 0x4;
        nProt1 = IPX_BIT;
        err = NO_ERROR;
    }
    else
    {
        
        LPGUID pgClass = lpqsRestrictions->lpServiceClassId;

        if(!pgClass
              ||
           GuidEqual(pgClass, &AddressGuid)
              ||
           GuidEqual(pgClass, &IANAGuid) )
        {
            goto BadGuid;
        }

        if(!lpqsRestrictions->lpszServiceInstanceName
                          ||
           !*lpqsRestrictions->lpszServiceInstanceName)
        {
            SetLastError(WSA_INVALID_PARAMETER);
            return(SOCKET_ERROR);
        }
        if(!lpServiceClassInfo)
        {
           dwNumClassInfo = 0;
        }
        else
        {
            dwNumClassInfo = lpServiceClassInfo->dwCount;
            lpClassInfoBuf = lpServiceClassInfo->lpClassInfos;
        }

        err = pLocateSapIdAndProtocls(pgClass,
                                      dwNumClassInfo,
                                      lpClassInfoBuf,
                                      &wSapid,
                                      &nProt1);
        if(err)
        {
            if(dwNumClassInfo)
            {
                SetLastError(err);
                return(SOCKET_ERROR);
            }
            else
            {
                nProt1 = nProt;
                wSapid = 0;
                err = 0;
            }
        }
    }

    nProt &= nProt1;              // the relevant protocols

    if(!nProt)
    {
        SetLastError(WSANO_DATA);
        return(SOCKET_ERROR);  
    }

    //
    // Make sure a class ID is given since we copy it
    //

    if(!lpqsRestrictions->lpServiceClassId)
    {
        //
        // not. So, fail
        //
        SetLastError(WSA_INVALID_PARAMETER);
        return(SOCKET_ERROR);
    }

    //
    // It looks like a query we can handle. Make a context
    //

    psrc = SapMakeContext(0,
                      sizeof(WSAVERSION) - sizeof(PVOID));
                  
    if(!psrc)
    {
        SetLastError(WSA_NOT_ENOUGH_MEMORY);
        return(SOCKET_ERROR);
    }

    //
    // save things
    //

    psrc->gdType = *lpqsRestrictions->lpServiceClassId;
    psrc->dwControlFlags = dwControlFlags;   // save for Next processing
    psrc->wSapId = wSapid;

    if(pwszContext)
    {
        wcscpy(psrc->wszContext, pwszContext);
    }

    //
    // save the relevant restrictions
    // if the name is a wild-card, don't copy it. A NULL name
    // serves as a wild-card to NSPLookupServiceNext
    //

    if(lpqsRestrictions->lpszServiceInstanceName
             &&
       *lpqsRestrictions->lpszServiceInstanceName
             &&
        wcscmp(lpqsRestrictions->lpszServiceInstanceName, L"*"))
    {
        DWORD dwLen = wcslen(lpqsRestrictions->lpszServiceInstanceName);
        if(dwLen > 48)
        {
            err = WSA_INVALID_PARAMETER;
            goto Done;
        }
        else
        {
            RtlMoveMemory(psrc->chwName,
                          lpqsRestrictions->lpszServiceInstanceName,
                          dwLen * sizeof(WCHAR));
            _wcsupr(psrc->chwName);
        }
    }
  
    psrc->fConnectionOriented = (DWORD) -1;

    *lphLookup = (HANDLE)psrc;
    psrc->nProt = nProt;
    psrc->gdProvider = *lpProviderId;
    if(lpqsRestrictions->lpVersion)
    {
        *(LPWSAVERSION)&psrc->pvVersion = *lpqsRestrictions->lpVersion;
    }
    
Done:
    SapReleaseContext(psrc);
    if(err != NO_ERROR)
    {
        SapReleaseContext(psrc);
        SetLastError(err);
        err = SOCKET_ERROR;
    }
    return(err);
}

INT WINAPI
NSPLookupServiceNext(
    IN     HANDLE          hLookup,
    IN     DWORD           dwControlFlags,
    IN OUT LPDWORD         lpdwBufferLength,
    OUT    LPWSAQUERYSETW  lpqsResults
    )
/*++
Routine Description:
    The continuation of the LookupServiceBegin. Called to fetch
    a matching item.
Arguments:
    See RnR spec
--*/
{
    DWORD err = NO_ERROR;
    PSAP_RNR_CONTEXT psrc;
    SOCKADDR_IPX SockAddr;
    WCHAR OutName[48];
    PBYTE pData = (PBYTE)(lpqsResults + 1);
    LONG lSpare;
    LONG lLastIndex;
    DWORD dwVersion;
    WSAQUERYSETW wsaqDummy;
    BOOL fDoStateMachine;

    if(*lpdwBufferLength < sizeof(WSAQUERYSETW))
    {
        lpqsResults = &wsaqDummy;
    }
    lSpare = (LONG)*lpdwBufferLength - sizeof(WSAQUERYSETW);

    memset(lpqsResults, 0, sizeof(WSAQUERYSETW));
    lpqsResults->dwNameSpace = NS_SAP;
    lpqsResults->dwSize = sizeof(WSAQUERYSETW);
    psrc = SapGetContext(hLookup);
    if(!psrc)
    {
  
        SetLastError(WSA_INVALID_HANDLE);
        return(SOCKET_ERROR);
    }

    //
    // This is a valid context. Determine whether this is the first
    // call to this. If so, we need to determine whether to
    // get the information from the bindery or by using SAP.
    //

    if ( psrc->u_type.bc.lIndex == 0xffffffff )
    {
        err = WSA_E_NO_MORE;
        goto DoneNext;
    }

    //
    // make sure we have the class info info
    //

    if(!psrc->wSapId)
    {
        //
        // Need to get it
        //

        UCHAR Buffer[1000];
        LPWSASERVICECLASSINFOW lpcli = (LPWSASERVICECLASSINFOW)Buffer;
        DWORD dwBufSize;
        DWORD nProt1;

        dwBufSize = 1000;
        lpcli->lpServiceClassId = &psrc->gdType;

        if( (err = NSPGetServiceClassInfo(&psrc->gdProvider,
                                  &dwBufSize,
                                  lpcli)) != NO_ERROR)
        {
            goto DoneNext;
        }

        err = pLocateSapIdAndProtocls(&psrc->gdType,
                                      lpcli->dwCount,
                                      lpcli->lpClassInfos,
                                      &psrc->wSapId,
                                      &nProt1);
        if(err)
        {
            SetLastError(err);
            goto DoneNext;
        }

        psrc->nProt &= nProt1;
        if(!psrc->nProt)
        {
            //
            // no protocols match
            //

            err = WSANO_DATA;
            goto DoneNext;
        }
    }

    //
    // this is the state machine for querying. It selects the bindery or
    // SAP as appropriate.
    //


    fDoStateMachine = TRUE;         // enter the machine

    do
    {
        //
        // switch on the current machine state.
        //
        switch(psrc->dwUnionType)
        {

        case LOOKUP_TYPE_NIL:
            psrc->u_type.bc.lIndex = -1;
            psrc->dwUnionType = LOOKUP_TYPE_BINDERY;
            break;                 // reenter state machine

        case LOOKUP_TYPE_BINDERY:

            //
            // try the bindery
            //


            if(psrc->dwControlFlags & LUP_NEAREST)
            {
                err = NO_DATA;        // skip the bindery
            }
            else
            {
                //
                // otherwise, try the bindery
                //

 
                EnterCriticalSection(&psrc->u_type.sbc.csMonitor);

                lLastIndex = psrc->u_type.bc.lIndex;   // save it


                err = NwpGetRnRAddress(
                             &psrc->hServer,
                             (psrc->wszContext[0] ?
                                     &psrc->wszContext[0] :
                                     0),
                             &psrc->u_type.bc.lIndex,
                             (psrc->chwName[0] ?
                                  psrc->chwName :
                                  0),
                             psrc->wSapId,
                             &dwVersion,
                             48 * sizeof(WCHAR),
                             OutName,
                             &SockAddr);

                LeaveCriticalSection(&psrc->u_type.sbc.csMonitor);
            }

            if(err != NO_ERROR)
            {

                if((psrc->u_type.bc.lIndex == -1))
                {
                    err = PrepareForSap(psrc);
                    if(err)
                    {
                        //
                        // if we can't, exit the state machine
                        //
                        fDoStateMachine = FALSE;
                    }
                }
                else
                {
                    //
                    // no more bindery entries. We will leave the state machine
                    //

                    if(err == ERROR_NO_MORE_ITEMS)
                    {
                        err = WSA_E_NO_MORE;
                    }
                    fDoStateMachine = FALSE;
                }
                break;
            }
            else
            {
                LPWSAVERSION lpVersion = (LPWSAVERSION)&psrc->pvVersion;

                if(lpVersion->dwVersion && dwVersion)
                {
                    //
                    // need to checkout for version matching
                    //

                    switch(lpVersion->ecHow)
                    {
                        case COMP_EQUAL:
                            if(lpVersion->dwVersion != dwVersion)
                            {
                               continue;   //reenter machine
                            }
                            break;

                        case COMP_NOTLESS:
                            if(lpVersion->dwVersion > dwVersion)
                            {
                                continue;
                            }
                            break;

                        default:
                            continue;         // error. If we don't
                                              // know how to compare, we
                                              // must  reject it.
                    }
                }

                //
                // have a suitable item.
                // return the name and type and all
                // that

                err = pRnRReturnResults(
                           OutName,
                           &psrc->gdType,
                           dwVersion,
                           &pData,
                           &lSpare,
                           (PBYTE)SockAddr.sa_netnum,
                           psrc->nProt,
                           psrc->dwControlFlags,
                           lpqsResults);
 
                if(err == WSAEFAULT)
                {
                    //
                    // no room. Return buffer size required and
                    // restore the index
                    //

                    *lpdwBufferLength =
                       (DWORD)((LONG)*lpdwBufferLength - lSpare);
                    psrc->u_type.bc.lIndex = lLastIndex;
            
                }
                fDoStateMachine = FALSE;
            }
            break;

       case LOOKUP_TYPE_SAP:

            //
            // Use SAP.
            //

            {
                WORD QueryType;

                if(psrc->dwControlFlags & LUP_NEAREST)
                {
                    QueryType = QT_NEAREST_QUERY;
                }
                else
                {
                    QueryType = QT_GENERAL_QUERY;
                }

                err = DoASap(psrc,
                             QueryType,
                             &pData,
                             lpqsResults,
                             &lSpare,
                             lpdwBufferLength);

                if((err == WSA_E_NO_MORE)
                        &&
                   !(psrc->fFlags & SAP_F_END_CALLED)
                        &&
                   (QueryType == QT_NEAREST_QUERY) 
                        &&
                   (psrc->dwControlFlags & LUP_DEEP)
                        &&
                    !psrc->u_type.sbc.psdHead)
                {
                    //
                    // didn't find it locally. Turn off LUP_NEAREST
                    // and do this as a general query. This might bring
                    // it back to a SAP query, but this time
                    // without LUP_NEAREST. But starting anew
                    // allows the use of the bindery and that
                    // might find things quickly.
                    //
                    psrc->dwControlFlags &= ~LUP_NEAREST;
                    psrc->dwUnionType = LOOKUP_TYPE_NIL;
                    if(psrc->u_type.sbc.s)
                    {
                        SapFreeSapSocket(psrc->u_type.sbc.s);
                        psrc->u_type.sbc.s = 0;
                    }

                }
                else
                {
                    fDoStateMachine = FALSE;
                }
                break;
            }
        }    // switch
    } while(fDoStateMachine);

DoneNext:
    SapReleaseContext(psrc);
    if((err != NO_ERROR)
            &&
       (err != (DWORD)SOCKET_ERROR))
    {
        SetLastError(err);
        err = (DWORD)SOCKET_ERROR;
    }
    return((INT)err);
}

BOOL
NSPpCheckCancel(
    PVOID pvArg
    )
/*++
Routine Description:
    Coroutine called to check if the SAP lookup has been cancelled.
    For now, this always returns FALSE as we use the flags word in
    the control block instead
--*/
{
    return(FALSE);
}


DWORD
NSPpGotSap(
    PSAP_BCAST_CONTROL psbc,
    PSAP_IDENT_HEADER pSap,
    PDWORD pdwErr
    )
/*++
Routine Description:
    Coroutine called for each SAP reply received. This decides
    whether to keep the data or not and returns a code telling
    the SAP engine whether to proceed

Arguments:
    psbc  --  the SAP_BCAST_CONTROL
    pSap  --  the SAP reply
    pdwErr -- where to put an error code
--*/
{
    PSAP_DATA psdData;
    LPWSAQUERYSETW Results = (LPWSAQUERYSETW)psbc->pvArg;
    PSAP_RNR_CONTEXT psrc = psbc->psrc;
    DWORD dwRet = dwrcNil;
    PCHAR pServiceName = (PCHAR)psrc->chName;
    
    EnterCriticalSection(&psbc->csMonitor);

    //
    // First, check if this is a lookup for a particular name. If so,
    // accept only the name.
    //

    if(*pServiceName)
    {
        if(strcmp(pServiceName, pSap->ServerName))
        {
            goto nota;
        }
        if(!(psrc->dwControlFlags & LUP_NEAREST))
        {
            dwRet = dwrcDone;
            psbc->fFlags |= SBC_FLAG_NOMORE;
        }
    }

    //
    // see if we want to keep this guy
    // We keep it if we don't already have it in the list
    //


    for(psdData = psbc->psdHead;
        psdData;
        psdData = psdData->sapNext)
    {
        if(RtlEqualMemory(  psdData->socketAddr,
                            &pSap->Address,
                            IPX_ADDRESS_LENGTH))
        {
            goto nota;          // we have it already
        }
    }

    psdData = (PSAP_DATA)LocalAlloc(LPTR, sizeof(SAP_DATA));
    if(!psdData)
    {
        goto nota;            // can't save it
    }

    psdData->sapid = pSap->ServerType;
    RtlMoveMemory(psdData->sapname,
                  pSap->ServerName,
                  48);
    RtlMoveMemory(psdData->socketAddr,
                  &pSap->Address,
                  IPX_ADDRESS_LENGTH);

    if(psbc->psdTail)
    {
        psbc->psdTail->sapNext = psdData;
    }
    else
    {
        psbc->psdHead = psdData;
    }
    psbc->psdTail = psdData;
    if(!psbc->psdNext1)
    {
        psbc->psdNext1 = psdData;
    }

nota:

    LeaveCriticalSection(&psbc->csMonitor);

    if((dwRet == dwrcNil)
             &&
       psbc->psdNext1)
    {
        dwRet = dwrcNoWait;
    }
    return(dwRet);
}

INT WINAPI
NSPUnInstallNameSpace(
    IN LPGUID lpProviderId
    )
{
    return(NO_ERROR);
}

INT WINAPI
NSPCleanup(
    IN LPGUID lpProviderId
    )
{
    //
    // Make sure all contexts are released
    //

//    pFreeAllContexts();   // done in Dll Process detach
    return(NO_ERROR);
}

INT WINAPI
NSPLookupServiceEnd(
    IN HANDLE hLookup
    )
{
    PSAP_RNR_CONTEXT psrc;

    psrc = SapGetContext(hLookup);
    if(!psrc)
    {
  
        SetLastError(WSA_INVALID_HANDLE);
        return(SOCKET_ERROR);
    }

    psrc->fFlags |= SAP_F_END_CALLED;
    SapReleaseContext(psrc);         // get rid of it
    SapReleaseContext(psrc);         // and close it. Context cleanup is
                                     //  done on the last derefernce.
    return(NO_ERROR);
}

INT WINAPI
NSPSetService(
    IN  LPGUID               lpProviderId,
    IN  LPWSASERVICECLASSINFOW  lpServiceClassInfo,
    IN LPWSAQUERYSETW lpqsRegInfo,
    IN WSAESETSERVICEOP essOperation,
    IN DWORD          dwControlFlags
    )
{
/*++
Routine Description:
   The routine that implements the RnR SetService routine. Note that
   context is ignored. There is no way to direct the registration to
   a particular server.
--*/
    PBYTE pbAddress;
    DWORD dwOperation;
    PBYTE pbSocket;
    DWORD dwAddrs;
    DWORD err = NO_ERROR;
    WORD wSapId;
    DWORD nProt, dwAddress;
    BOOL fAdvert = FALSE;
    DWORD                dwNumClassInfo;
    LPWSANSCLASSINFOW    lpClassInfoBuf;


    //
    // Verify all args present
    //

    if(!lpqsRegInfo->lpszServiceInstanceName
               ||
       !lpqsRegInfo->lpServiceClassId)
    {
        SetLastError(WSA_INVALID_PARAMETER);
        return(SOCKET_ERROR);
    }

    if(!lpServiceClassInfo && !IS_SVCID_NETWARE(lpqsRegInfo->lpServiceClassId))
    {
        UCHAR Buffer[1000];
        LPWSASERVICECLASSINFOW lpcli = (LPWSASERVICECLASSINFOW)Buffer;
        DWORD dwBufSize;

        dwBufSize = 1000;
        lpcli->lpServiceClassId = lpqsRegInfo->lpServiceClassId;

        if(pGetServiceClassInfo(lpProviderId,
                                  &dwBufSize,
                                  lpcli,
                                  &fAdvert) != NO_ERROR)
        {
            return(SOCKET_ERROR);
        }
        dwNumClassInfo = lpcli->dwCount;
        lpClassInfoBuf = lpcli->lpClassInfos;
    }
    else if (lpServiceClassInfo)
    {
        dwNumClassInfo = lpServiceClassInfo->dwCount;
        lpClassInfoBuf = lpServiceClassInfo->lpClassInfos;
    }
    else
    {
        // lpServiceClassId is a GUID which defines the SapId.  This means
        // that pLocateSapIdAndProtocls doesn't need the lpClassInfoBuf.
        dwNumClassInfo = 0;
        lpClassInfoBuf = 0;
    }

    //
    // Find the IPX address in the input args
    //

    err = pLocateSapIdAndProtocls(lpqsRegInfo->lpServiceClassId,
                                  dwNumClassInfo,
                                  lpClassInfoBuf,
                                  &wSapId,
                                  &nProt);
                                  
    if(err == NO_ERROR)
    {
        if(essOperation == RNRSERVICE_REGISTER)
        {
            PCSADDR_INFO pcsaAddress;

            pcsaAddress = lpqsRegInfo->lpcsaBuffer;

            try
            {
                for(dwAddrs = lpqsRegInfo->dwNumberOfCsAddrs;
                    dwAddrs;
                    dwAddrs--, pcsaAddress++)
                {
                    if(pcsaAddress->LocalAddr.lpSockaddr->sa_family == AF_IPX)
                    {
                        pbSocket = 
                               (PBYTE)pcsaAddress->LocalAddr.lpSockaddr;
                        break;
                    }
                }
            }
            except(EXCEPTION_EXECUTE_HANDLER)
            {
                err = GetExceptionCode();
            }

            if(err || !dwAddrs)
            {
                err = ERROR_INCORRECT_ADDRESS;
            }
            else if(fAdvert)
            {
               NwpSetClassInfoAdvertise(lpqsRegInfo->lpServiceClassId,
                                        wSapId);
            }
        }
        else
        {
            pbSocket = 0;
        }
        if(err == NO_ERROR)
        {
            //
            // map the operation, and call the common worker
            //

            switch(essOperation)
            {
                case RNRSERVICE_REGISTER:
                    dwOperation = oldRnRServiceRegister;
                    break;
                case RNRSERVICE_DEREGISTER:
                case RNRSERVICE_DELETE:
                    dwOperation = oldRnRServiceDeRegister;
                    break;
                default:
                    err = WSA_INVALID_PARAMETER;
                    break;
            }
            if(err == NO_ERROR)
            {
                err = pSapSetService2(
                           dwOperation,
                           lpqsRegInfo->lpszServiceInstanceName,
                           pbSocket,
                           lpqsRegInfo->lpServiceClassId,
                           wSapId);
                   
            }
        }
    }
    if(err != NO_ERROR)
    {
        SetLastError(err);
        err = (DWORD)SOCKET_ERROR;
    }
    return(err);
}

INT WINAPI
NSPInstallServiceClass(
    IN  LPGUID               lpProviderId,
    IN LPWSASERVICECLASSINFOW lpServiceClassInfo
    )
{
    LPWSANSCLASSINFOW pcli, pcli1 = 0;
    DWORD dwIndex = lpServiceClassInfo->dwCount;
    CHAR PropertyBuffer[128];
    PBINDERYCLASSES pbc = (PBINDERYCLASSES)PropertyBuffer;
    BYTE bData = (BYTE)&((PBINDERYCLASSES)0)->cDataArea[0];
    PCHAR pszData = &pbc->cDataArea[0];
    DWORD err;
    DWORD iter;
    WORD port = 0, sap = 0;
    BOOL fIsSAP = FALSE;

    //
    // Check a few parameters . . .
    //
    if ( lpServiceClassInfo == NULL )
    {
        SetLastError(WSA_INVALID_PARAMETER);
        return(SOCKET_ERROR);
    }

    if ( !lpServiceClassInfo->lpServiceClassId ||
         !lpServiceClassInfo->lpszServiceClassName ||
         ( lpServiceClassInfo->dwCount &&
           !lpServiceClassInfo->lpClassInfos ) )
    {
        SetLastError(WSA_INVALID_PARAMETER);
        return(SOCKET_ERROR);
    }

    //
    // Test to see if the ServiceClassId is TCP's, if so we don't allow
    // the service class installation since these are already present.
    //
    if ( GuidEqual( lpServiceClassInfo->lpServiceClassId,
                     &HostAddrByInetStringGuid ) ||
         GuidEqual( lpServiceClassInfo->lpServiceClassId,
                     &ServiceByNameGuid ) ||
         GuidEqual( lpServiceClassInfo->lpServiceClassId,
                     &HostNameGuid ) ||
         GuidEqual( lpServiceClassInfo->lpServiceClassId,
                     &HostAddrByNameGuid ) )
    {
        SetLastError(WSA_INVALID_PARAMETER);
        return(SOCKET_ERROR);
    }

    for( iter = 0; iter < lpServiceClassInfo->dwCount; iter++ )
    {
        if ( lpServiceClassInfo->lpClassInfos[iter].dwNameSpace == NS_SAP ||
             lpServiceClassInfo->lpClassInfos[iter].dwNameSpace == NS_ALL )
            fIsSAP = TRUE;
    }

    if ( !fIsSAP )
    {
        SetLastError(WSA_INVALID_PARAMETER);
        return(SOCKET_ERROR);
    }

    //
    // Find the SapId entry
    //

    for(pcli = lpServiceClassInfo->lpClassInfos;
        dwIndex;
        pcli++, dwIndex--)
    {
        WORD wTemp;

        if ( pcli->dwNameSpace == NS_SAP ||
             pcli->dwNameSpace == NS_ALL )
        {
            if(!_wcsicmp(pcli->lpszName, SERVICE_TYPE_VALUE_IPXPORTW)
                     &&
               (pcli->dwValueSize == sizeof(WORD)))
            {
                //
                // the value may not be aligned
                //
                ((PBYTE)&wTemp)[0] = ((PBYTE)pcli->lpValue)[0];
                ((PBYTE)&wTemp)[1] = ((PBYTE)pcli->lpValue)[1];
                port = wTemp;
            } else if(!_wcsicmp(pcli->lpszName, SERVICE_TYPE_VALUE_SAPIDW)
                     &&
               (pcli->dwValueSize >= sizeof(WORD)))
            {
                ((PBYTE)&wTemp)[0] = ((PBYTE)pcli->lpValue)[0];
                ((PBYTE)&wTemp)[1] = ((PBYTE)pcli->lpValue)[1];
                sap = wTemp;
                pcli1 = pcli;
            }
        }
    }

    if(!(pcli = pcli1))
    {
        SetLastError(WSA_INVALID_PARAMETER);
        return(SOCKET_ERROR);
    }

#if 0                // old way of doing this
    //
    // Found it. Build the property segment
    //

    memset(PropertyBuffer, 0, 128);   // clear out everyting

    pbc->bOffset = bData;
    pbc->bSizeOfString = sizeof("Sapid");

    pbc->bType = BT_WORD;            // just a word, I assure you
    pbc->bSizeOfType = 2;
    pbc->wNameSpace = (WORD)NS_SAP;  // it's us
    *(PWORD)pszData = htons(*(PWORD)pcli->lpValue);
    pszData += sizeof(WORD);         // where the string goes
    strcpy(pszData, "SapId");
//    pbc->bFlags = (BYTE)pcli->dwConnectionFlags;

    err = NwpSetClassInfo(
                   lpServiceClassInfo->lpszServiceClassName,
                   lpServiceClassInfo->lpServiceClassId,
                   PropertyBuffer);
#else
    err = NwpRnR2AddServiceType(
               lpServiceClassInfo->lpszServiceClassName,
               lpServiceClassInfo->lpServiceClassId,
               sap,
               port);
#endif
    if(err != NO_ERROR)
    {
        SetLastError(err);
        err = (DWORD)SOCKET_ERROR;
    }
                
    return(err);
}

INT WINAPI
NSPRemoveServiceClass(
    IN  LPGUID lpProviderId,
    IN  LPGUID lpServiceCallId
    )
{
    BOOL success;

    //
    // Do parameter checking
    //
    if ( lpServiceCallId == NULL )
    {
        SetLastError( WSA_INVALID_PARAMETER );
        return SOCKET_ERROR;
    }

    success = NwpRnR2RemoveServiceType( lpServiceCallId );

    if ( success )
        return( NO_ERROR );
    else
        SetLastError(WSATYPE_NOT_FOUND);
        return (DWORD)SOCKET_ERROR;
}

INT WINAPI
NSPGetServiceClassInfo(
    IN  LPGUID               lpProviderId,
    IN OUT LPDWORD    lpdwBufSize,
    IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo
    )
{
/*++
Routine Description:
    Get the ClassInfo for this type. Class info data may be in the
    registry, or available via SAP or the bindery. We try all three
    as appropriate
--*/
    BOOL fAdvert;

    return(pGetServiceClassInfo(
                  lpProviderId,
                  lpdwBufSize,
                  lpServiceClassInfo,
                  &fAdvert));
}

INT WINAPI
pGetServiceClassInfo(
    IN  LPGUID               lpProviderId,
    IN OUT LPDWORD    lpdwBufSize,
    IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo,
    IN  PBOOL          pfAdvert
    )
{
/*++
Routine Description:
    Get the ClassInfo for this type. Class info data may be in the
    registry, or available via SAP or the bindery. We try all three
    as appropriate
--*/
    DWORD err;
    LONG lInSize;
    LONG lSizeNeeded;
    PBYTE pbBuffer;
    GUID gdDummy;
    PWCHAR pwszUuid;
    LPGUID pType;
    WORD wSapId;
    WORD wPort;
    SOCKADDR_IPX sock;
    PWCHAR pwszSaveName = lpServiceClassInfo->lpszServiceClassName;

#define SIZENEEDED (sizeof(WSASERVICECLASSINFO) +   \
                    sizeof(WSANSCLASSINFO) + \
                    sizeof(WSANSCLASSINFO) + \
                    10 + 2                 + \
                    sizeof(GUID) +  12 + 2)

    *pfAdvert = FALSE;

    lInSize = (LONG)*lpdwBufSize - SIZENEEDED;

    pType = (LPGUID)(lpServiceClassInfo + 1);

    pbBuffer = (PBYTE)(pType + 1);

    if(lInSize < 0)
    {
        //
        // it is too small already
        //

        pType = &gdDummy;
    }

    if (UuidToString(lpServiceClassInfo->lpServiceClassId, &pwszUuid) != RPC_S_OK) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return SOCKET_ERROR;
    }

    //
    // First, try the bindery
    //

    err = NwpGetAddressByName(0,
                              RNRCLASSSAPTYPE,
                              pwszUuid,
                              &sock);

    if(err == NO_ERROR)
    {
        wSapId = ntohs(*(PWORD)&sock.sa_netnum);
        wPort = ntohs(*(PWORD)&sock.sa_socket);
    }
    else
    {
        UCHAR Buffer[400];
        DWORD dwLen = 400;
        DWORD dwNum;
        //
        // try SAP
        //

        err = NwpGetAddressViaSap(RNRCLASSSAPTYPE,
                                  pwszUuid,
                                  IPX_BIT,
                                  (PVOID)Buffer,
                                  &dwLen,
                                  0,
                                  &dwNum);
        if((err == NO_ERROR)
                 &&
            (dwNum > 0) )
        {
            PCSADDR_INFO psca = (PCSADDR_INFO)Buffer;
            PSOCKADDR_IPX psock = (PSOCKADDR_IPX)psca->RemoteAddr.lpSockaddr;

            wSapId = ntohs(*(PWORD)&psock->sa_netnum);
            wPort = ntohs(*(PWORD)&sock.sa_socket);
        }
        else
        {
            //
            // try the local bindery
    
    
            if(!NwpLookupSapInRegistry(
                 lpServiceClassInfo->lpServiceClassId,
                 &wSapId,
                 &wPort,
                 NULL))
            {
                err = WSASERVICE_NOT_FOUND;
            }
            else
            {
                *pfAdvert = TRUE;
                err = NO_ERROR;
            }
        }
    }
    
    RpcStringFree(&pwszUuid);

    if(err != NO_ERROR)
    {
        SetLastError(err);
        err = (DWORD)SOCKET_ERROR;
    }
    else
    {
        //
        // we return the input structure and the found type. That's it.
        // The space needed is a constant since we don't return the name
        //

        if(lInSize < 0)
        {
            SetLastError(WSAEFAULT);
            *lpdwBufSize += (DWORD)(-lInSize);
            err = (DWORD)SOCKET_ERROR;
        }
        else
        {
            LPWSANSCLASSINFOW pci = (LPWSANSCLASSINFOW)pbBuffer;
            PUCHAR Buff;
            //
            // it will fit. SO let's go
            //

            if(wPort)
            {
                Buff = (PCHAR)(pci + 2);
            }
            else
            {
                Buff = (PCHAR)(pci + 1);
            }

            *pType = *lpServiceClassInfo->lpServiceClassId;
            lpServiceClassInfo->lpServiceClassId = pType;
            lpServiceClassInfo->lpszServiceClassName = 0;   // not a
            lpServiceClassInfo->dwCount = 1;
            lpServiceClassInfo->lpClassInfos = pci;
            pci->dwNameSpace = NS_SAP;
            pci->dwValueType = REG_DWORD;
            pci->dwValueSize = 2;
            pci->lpszName = (LPWSTR)Buff;
            wcscpy((PWCHAR)Buff, L"SapId");
            Buff += 6 * sizeof(WCHAR);
            pci->lpValue = (LPVOID)Buff;
            *(PWORD)Buff = wSapId;
            Buff += sizeof(WORD);
            if(wPort)
            {
                lpServiceClassInfo->dwCount++;
                pci++;
                pci->dwNameSpace = NS_SAP;
                pci->dwValueType = REG_DWORD;
                pci->dwValueSize = 2;
                pci->lpszName = (LPWSTR)Buff;
                wcscpy((PWCHAR)Buff, L"Port");
                Buff += 5 * sizeof(WCHAR);
                pci->lpValue = (LPVOID)Buff;
                *(PWORD)Buff = wPort;
            }
        }
    }
    return(err);
}

INT WINAPI
NSPStartup(
    IN LPGUID         lpProviderId,
    IN OUT LPNSP_ROUTINE lpsnpRoutines)
{
//    DWORD dwSize = min(sizeof(nsrVector), lpsnpRoutines->cbSize);
    DWORD dwSize = sizeof(nsrVector);
    RtlCopyMemory(lpsnpRoutines,
                  &nsrVector,
                  dwSize);
    return(NO_ERROR);
}

DWORD
DoASap(
    IN PSAP_RNR_CONTEXT psrc,
    IN WORD QueryType,
    IN PBYTE * ppData,
    IN LPWSAQUERYSETW lpqsResults,
    IN PLONG   plSpare,
    IN PDWORD  lpdwBufferLength
    )
/*++
   Small routine to construcst a SAP_BROADCAST pakcet and issue the SAP
--*/
{
    DWORD err;

    if(!psrc->u_type.sbc.s)
    {
        //
        // this is the first time. We must init the
        // structure
        //
        err = SapGetSapSocket(&psrc->u_type.sbc.s);
        if(err)
        {
            psrc->u_type.sbc.s = 0;    // make sure
            return(err);
        }
        psrc->u_type.sbc.Func = NSPpGotSap;
        psrc->u_type.sbc.fCheckCancel = NSPpCheckCancel;
        psrc->u_type.sbc.dwIndex = 0;    // just in case.
        psrc->u_type.sbc.pvArg = (PVOID)lpqsResults;
        psrc->u_type.sbc.psrc = psrc;
        psrc->u_type.sbc.fFlags = 0;

    }

    psrc->u_type.sbc.wQueryType = QueryType;

    if(!psrc->u_type.sbc.psdNext1
                &&
       !(psrc->u_type.sbc.fFlags & SBC_FLAG_NOMORE))
    {
        err = SapGetSapForType(&psrc->u_type.sbc, psrc->wSapId);
    }

    EnterCriticalSection(&psrc->u_type.sbc.csMonitor);
    if(psrc->u_type.sbc.psdNext1)
    {
        //
        // Got something to return.  Let's do it
        //


        //
        // Assume we have to return the name
        //

               
        //
        // We have to convert the name to UNICODE so
        // we can return it to the caller.
        //
        //

        OEM_STRING Oem;
        NTSTATUS status;
        UNICODE_STRING UString;

        RtlInitAnsiString(&Oem,
                          psrc->u_type.sbc.psdNext1->sapname);
        status = RtlOemStringToUnicodeString(
                            &UString,
                            &Oem,
                            TRUE);
        if(NT_SUCCESS(status))
        {
            if(psrc->wSapId == OT_DIRSERVER)
            {
                PWCHAR pwszTemp = &UString.Buffer[31];

                while(*pwszTemp == L'_')
                {
                    *pwszTemp-- = 0;
                }
            }
            err = pRnRReturnResults(
                       UString.Buffer,
                       &psrc->gdType,
                       0,            // never a version
                       ppData,
                       plSpare,
                       psrc->u_type.sbc.psdNext1->socketAddr,
                       psrc->nProt,
                       psrc->dwControlFlags,
                       lpqsResults);
            RtlFreeUnicodeString(&UString);
            if(err == WSAEFAULT)
            {
                //
                // no room. Return buffer size required
                //

                *lpdwBufferLength =
                    (DWORD)((LONG)*lpdwBufferLength - *plSpare);
            }
        }
        else
        {
            err = (DWORD)status;
        }
        if(err == NO_ERROR)
        {
            //
            // if we got it, step the item
            //
            psrc->u_type.sbc.psdNext1 =
                psrc->u_type.sbc.psdNext1->sapNext;
        }
        

    }
    else
    {
         err = (DWORD)WSA_E_NO_MORE;
    }
    LeaveCriticalSection(&psrc->u_type.sbc.csMonitor);
    return(err);
}


DWORD
PrepareForSap(
    IN PSAP_RNR_CONTEXT psrc
    )
/*++
Called when there is no bindery or the bindery does not have the
entry. This initializes the values needed for a SAP search
--*/
{


    OEM_STRING OemServiceName;
    UNICODE_STRING UString;
    NTSTATUS status;

    //
    // the bindery didn't work. Use SAP.
    //


    psrc->u_type.sbc.dwIndex =
      psrc->u_type.sbc.dwTickCount = 0;
    if(psrc->wszContext[0])
    {
        return(WSASERVICE_NOT_FOUND);   // no contexts in SAP
    }
    RtlInitUnicodeString(&UString,
                         psrc->chwName);
    status = RtlUnicodeStringToOemString(&OemServiceName,
                                         &UString,
                                         TRUE);
    if(!NT_SUCCESS(status))
    {
       return( (DWORD)status);
    }
    strcpy((PCHAR)&psrc->chName,
            OemServiceName.Buffer);
    RtlFreeOemString(&OemServiceName);
    psrc->dwUnionType = LOOKUP_TYPE_SAP;
    psrc->u_type.sbc.s = 0;
    return(NO_ERROR);
}