diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/nw/svcdlls/nwwks/client/getaddr.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/nw/svcdlls/nwwks/client/getaddr.c')
-rw-r--r-- | private/nw/svcdlls/nwwks/client/getaddr.c | 3619 |
1 files changed, 3619 insertions, 0 deletions
diff --git a/private/nw/svcdlls/nwwks/client/getaddr.c b/private/nw/svcdlls/nwwks/client/getaddr.c new file mode 100644 index 000000000..2673eddae --- /dev/null +++ b/private/nw/svcdlls/nwwks/client/getaddr.c @@ -0,0 +1,3619 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + getaddr.c + +Abstract: + + This module contains the code to support NPGetAddressByName. + +Author: + + Yi-Hsin Sung (yihsins) 18-Apr-94 + Glenn A. Curtis (glennc) 31-Jul-95 + Arnold Miller (ArnoldM) 7-Dec-95 + +Revision History: + + yihsins Created + glennc Modified 31-Jul-95 + ArnoldM Modified 7-Dec-95 + +--*/ + + +#include <nwclient.h> +#include <winsock.h> +#include <wsipx.h> +#include <nspapi.h> +#include <nspapip.h> +#include <wsnwlink.h> +#include <svcguid.h> +#include <nwsap.h> +#include <align.h> +#include <nwmisc.h> +#include <rnrdefs.h> + +//-------------------------------------------------------------------// +// // +// Local Function Prototypes // +// // +//-------------------------------------------------------------------// + +#define NW_SAP_PRIORITY_VALUE_NAME L"SapPriority" +#define NW_WORKSTATION_SVCPROVIDER_REGKEY L"System\\CurrentControlSet\\Services\\NWCWorkstation\\ServiceProvider" + +#define NW_GUID_VALUE_NAME L"GUID" +#define NW_SERVICETYPES_KEY_NAME L"ServiceTypes" +#define NW_SERVICE_TYPES_REGKEY L"System\\CurrentControlSet\\Control\\ServiceProvider\\ServiceTypes" + +#define DLL_VERSION 1 +#define WSOCK_VER_REQD 0x0101 + +// +// critical sections used +// + +extern CRITICAL_SECTION NwServiceListCriticalSection; + + // have been returned +BOOL +OldRnRCheckCancel( + PVOID pvArg + ); + +DWORD +OldRnRCheckSapData( + PSAP_BCAST_CONTROL psbc, + PSAP_IDENT_HEADER pSap, + PDWORD pdwErr + ); + +DWORD +SapGetSapForType( + PSAP_BCAST_CONTROL psbc, + WORD nServiceType + ); + +DWORD +SapFreeSapSocket( + SOCKET s + ); + +DWORD +SapGetSapSocket( + SOCKET * ppsocket + ); + +VOID +pFreeAllContexts(); + +PSAP_RNR_CONTEXT +SapGetContext( + IN HANDLE Handle + ); + +PSAP_RNR_CONTEXT +SapMakeContext( + IN HANDLE Handle, + IN DWORD dwExcess + ); + +VOID +SapReleaseContext( + PSAP_RNR_CONTEXT psrcContext + ); + +INT +SapGetAddressByName( + IN LPGUID lpServiceType, + IN LPWSTR lpServiceName, + IN LPDWORD lpdwProtocols, + IN DWORD dwResolution, + IN OUT LPVOID lpCsAddrBuffer, + IN OUT LPDWORD lpdwBufferLength, + IN OUT LPWSTR lpAliasBuffer, + IN OUT LPDWORD lpdwAliasBufferLength, + IN HANDLE hCancellationEvent +); + +DWORD +SapGetService ( + IN LPGUID lpServiceType, + IN LPWSTR lpServiceName, + IN DWORD dwProperties, + IN BOOL fUnicodeBlob, + OUT LPSERVICE_INFO lpServiceInfo, + IN OUT LPDWORD lpdwBufferLen +); + +DWORD +SapSetService ( + IN DWORD dwOperation, + IN DWORD dwFlags, + IN BOOL fUnicodeBlob, + IN LPSERVICE_INFO lpServiceInfo +); + +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 +); + +BOOL +NwpLookupSapInRegistry( + IN LPGUID lpServiceType, + OUT PWORD pnSapType, + OUT PWORD pwPort, + IN OUT PDWORD pfConnectionOriented +); + +DWORD +NwpRnR2AddServiceType( + IN LPWSTR lpServiceTypeName, + IN LPGUID lpClassType, + IN WORD wSapId, + IN WORD wPort +); + +DWORD +NwpAddServiceType( + IN LPSERVICE_INFO lpServiceInfo, + IN BOOL fUnicodeBlob +); + +DWORD +NwpDeleteServiceType( + IN LPSERVICE_INFO lpServiceInfo, + IN BOOL fUnicodeBlob +); + +DWORD +FillBufferWithCsAddr( + IN LPBYTE pAddress, + IN DWORD nProt, + IN OUT LPVOID lpCsAddrBuffer, + IN OUT LPDWORD lpdwBufferLength, + OUT LPDWORD pcAddress +); + +DWORD +AddServiceToList( + IN LPSERVICE_INFO lpServiceInfo, + IN WORD nSapType, + IN BOOL fAdvertiseBySap, + IN INT nIndexIPXAddress +); + +VOID +RemoveServiceFromList( + IN PREGISTERED_SERVICE pSvc +); + +DWORD +pSapSetService2( + IN DWORD dwOperation, + IN LPWSTR lpszServiceInstance, + IN PBYTE pbAddress, + IN LPGUID pType, + IN WORD nServiceType + ); + +DWORD +pSapSetService( + IN DWORD dwOperation, + IN LPSERVICE_INFO lpServiceInfo, + IN WORD nServiceType + ); + +// +// Misc Functions +// + +DWORD NwInitializeSocket( + IN HANDLE hEventHandle +); + +DWORD +NwAdvertiseService( + IN LPWSTR pServiceName, + IN WORD nSapType, + IN LPSOCKADDR_IPX pAddr, + IN HANDLE hEventHandle +); + +DWORD SapFunc( + IN HANDLE hEventHandle +); + +DWORD +NwpGetAddressByName( + IN LPWSTR Reserved, + IN WORD nServiceType, + IN LPWSTR lpServiceName, + IN OUT LPSOCKADDR_IPX lpsockaddr +); + + + +//-------------------------------------------------------------------// +// // +// Global variables // +// // +//-------------------------------------------------------------------// + +// +// This is the address we send to +// + +UCHAR SapBroadcastAddress[] = { + AF_IPX, 0, // Address Family + 0x00, 0x00, 0x00, 0x00, // Dest. Net Number + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Dest. Node Number + 0x04, 0x52, // Dest. Socket + 0x04 // Packet type +}; + +PSAP_RNR_CONTEXT psrcSapContexts; + +// +// Misc. variables used if we need to advertise ourselves, i.e. +// when the SAP service is not installed/active. +// + +BOOL fInitSocket = FALSE; // TRUE if we have created the second thread +SOCKET socketSap; // Socket used to send SAP advertise packets +PREGISTERED_SERVICE pServiceListHead = NULL; // Points to head of link list +PREGISTERED_SERVICE pServiceListTail = NULL; // Points to tail of link list + +// +// needed to map old and new RnR functions +// +DWORD oldRnRServiceRegister = SERVICE_REGISTER; +DWORD oldRnRServiceDeRegister = SERVICE_DEREGISTER; + + +//-------------------------------------------------------------------// +// // +// Function Bodies // +// // +//-------------------------------------------------------------------// + +VOID +pFreeAllContexts() +/*++ +Routine Description: + Called at Cleanup time to free all NSP resource +--*/ +{ + PSAP_RNR_CONTEXT psrcContext; + + EnterCriticalSection( &NwServiceListCriticalSection ); + while(psrcContext = psrcSapContexts) + { + (VOID)SapReleaseContext(psrcContext); + } + LeaveCriticalSection( &NwServiceListCriticalSection ); +} + +PSAP_RNR_CONTEXT +SapGetContext(HANDLE Handle) +/*++ + +Routine Description: + + This routine checks the existing SAP contexts to see if we have one + for this calll. + +Arguments: + + Handle - the RnR handle, if appropriate + +--*/ +{ + PSAP_RNR_CONTEXT psrcContext; + + EnterCriticalSection( &NwServiceListCriticalSection ); + + for(psrcContext = psrcSapContexts; + psrcContext && (psrcContext->Handle != Handle); + psrcContext = psrcContext->pNextContext); + + if(psrcContext) + { + ++psrcContext->lInUse; + } + LeaveCriticalSection( &NwServiceListCriticalSection ); + return(psrcContext); +} + +PSAP_RNR_CONTEXT +SapMakeContext( + IN HANDLE Handle, + IN DWORD dwExcess + ) +{ +/*++ + +Routine Description: + + This routine makes a SAP conext for a given RnR handle + +Arguments: + + Handle - the RnR handle. If NULL, use the context as the handle + dwType - the type of the context + +--*/ + PSAP_RNR_CONTEXT psrcContext; + + psrcContext = (PSAP_RNR_CONTEXT) + LocalAlloc(LPTR, sizeof(SAP_RNR_CONTEXT) + + dwExcess); + if(psrcContext) + { + InitializeCriticalSection(&psrcContext->u_type.sbc.csMonitor); + psrcContext->lInUse = 2; + psrcContext->Handle = (Handle ? Handle : (HANDLE)psrcContext); + psrcContext->lSig = RNR_SIG; + EnterCriticalSection( &NwServiceListCriticalSection ); + psrcContext->pNextContext = psrcSapContexts; + psrcSapContexts = psrcContext; + LeaveCriticalSection( &NwServiceListCriticalSection ); + } + return(psrcContext); +} + +VOID +SapReleaseContext(PSAP_RNR_CONTEXT psrcContext) +/*++ + +Routine Description: + + Dereference an RNR Context and free it if it is no longer referenced. + Determining no referneces is a bit tricky because we try to avoid + obtaining the CritSec unless we think the context may be unneeded. Hence + the code goes through some fuss. It could be much simpler if we always + obtained the CritSec whenever we changed the reference count, but + this is faster for the nominal case. + +Arguments: + + psrcContext -- The context + +--*/ +{ + EnterCriticalSection( &NwServiceListCriticalSection ); + if(--psrcContext->lInUse == 0) + { + PSAP_RNR_CONTEXT psrcX, psrcPrev; + PSAP_DATA psdData; + + // + // Done with it. Remove from the lisgt + // + + psrcPrev = 0; + for(psrcX = psrcSapContexts; + psrcX; + psrcX = psrcX->pNextContext) + { + if(psrcX == psrcContext) + { + // + // Found it. + // + + if(psrcPrev) + { + psrcPrev->pNextContext = psrcContext->pNextContext; + } + else + { + psrcSapContexts = psrcContext->pNextContext; + } + break; + } + psrcPrev = psrcX; + } + + ASSERT(psrcX); + + // + // release SAP data, if any + // + if(psrcContext->dwUnionType == LOOKUP_TYPE_SAP) + { + for(psdData = psrcContext->u_type.sbc.psdHead; + psdData;) + { + PSAP_DATA psdTemp = psdData->sapNext; + + LocalFree(psdData); + psdData = psdTemp; + } + + if(psrcContext->u_type.sbc.s) + { + SapFreeSapSocket(psrcContext->u_type.sbc.s); + } + } + DeleteCriticalSection(&psrcContext->u_type.sbc.csMonitor); + if(psrcContext->hServer) + { + CloseHandle(psrcContext->hServer); + } + LocalFree(psrcContext); + } + LeaveCriticalSection( &NwServiceListCriticalSection ); +} + +INT +APIENTRY +NPLoadNameSpaces( + IN OUT LPDWORD lpdwVersion, + IN OUT LPNS_ROUTINE nsrBuffer, + IN OUT LPDWORD lpdwBufferLength + ) +{ +/*++ + +Routine Description: + + This routine returns name space info and functions supported in this + dll. + +Arguments: + + lpdwVersion - dll version + + nsrBuffer - on return, this will be filled with an array of + NS_ROUTINE structures + + lpdwBufferLength - on input, the number of bytes contained in the buffer + pointed to by nsrBuffer. On output, the minimum number of bytes + to pass for the nsrBuffer to retrieve all the requested info + +Return Value: + + The number of NS_ROUTINE structures returned, or SOCKET_ERROR (-1) if + the nsrBuffer is too small. Use GetLastError() to retrieve the + error code. + +--*/ + DWORD err; + DWORD dwLengthNeeded; + HKEY providerKey; + + DWORD dwSapPriority = NS_STANDARD_FAST_PRIORITY; + + *lpdwVersion = DLL_VERSION; + + // + // Check to see if the buffer is large enough + // + dwLengthNeeded = sizeof(NS_ROUTINE) + 4 * sizeof(LPFN_NSPAPI); + + if ( ( *lpdwBufferLength < dwLengthNeeded ) + || ( nsrBuffer == NULL ) + ) + { + *lpdwBufferLength = dwLengthNeeded; + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return (DWORD) SOCKET_ERROR; + } + + // + // Get the Sap priority from the registry. We will ignore all errors + // from the registry and have a default priority if we failed to read + // the value. + // + err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, + NW_WORKSTATION_SVCPROVIDER_REGKEY, + 0, + KEY_READ, + &providerKey ); + + if ( !err ) + { + DWORD BytesNeeded = sizeof( dwSapPriority ); + DWORD ValueType; + + err = RegQueryValueExW( providerKey, + NW_SAP_PRIORITY_VALUE_NAME, + NULL, + &ValueType, + (LPBYTE) &dwSapPriority, + &BytesNeeded ); + + if ( err ) // set default priority if error occurred + dwSapPriority = NS_STANDARD_FAST_PRIORITY; + } + + // + // We only support 1 name space for now, so fill in the NS_ROUTINE. + // + nsrBuffer->dwFunctionCount = 3; + nsrBuffer->alpfnFunctions = (LPFN_NSPAPI *) + ((BYTE *) nsrBuffer + sizeof(NS_ROUTINE)); + (nsrBuffer->alpfnFunctions)[NSPAPI_GET_ADDRESS_BY_NAME] = + (LPFN_NSPAPI) SapGetAddressByName; + (nsrBuffer->alpfnFunctions)[NSPAPI_GET_SERVICE] = + (LPFN_NSPAPI) SapGetService; + (nsrBuffer->alpfnFunctions)[NSPAPI_SET_SERVICE] = + (LPFN_NSPAPI) SapSetService; + (nsrBuffer->alpfnFunctions)[3] = NULL; + + nsrBuffer->dwNameSpace = NS_SAP; + nsrBuffer->dwPriority = dwSapPriority; + + return 1; // number of namespaces +} + +INT +SapGetAddressByName( + IN LPGUID lpServiceType, + IN LPWSTR lpServiceName, + IN LPDWORD lpdwProtocols, + IN DWORD dwResolution, + IN OUT LPVOID lpCsAddrBuffer, + IN OUT LPDWORD lpdwBufferLength, + IN OUT LPWSTR lpAliasBuffer, + IN OUT LPDWORD lpdwAliasBufferLength, + IN HANDLE hCancellationEvent + ) +/*++ + +Routine Description: + + This routine returns address information about a specific service. + +Arguments: + + lpServiceType - pointer to the GUID for the service type + + lpServiceName - unique string representing the service name, in the + Netware case, this is the server name + + lpdwProtocols - a zero terminated array of protocol ids. This parameter + is optional; if lpdwProtocols is NULL, information on all available + Protocols is returned + + dwResolution - can be one of the following values: + RES_SOFT_SEARCH, RES_FIND_MULTIPLE + + lpCsAddrBuffer - on return, will be filled with CSADDR_INFO structures + + lpdwBufferLength - on input, the number of bytes contained in the buffer + pointed to by lpCsAddrBuffer. On output, the minimum number of bytes + to pass for the lpCsAddrBuffer to retrieve all the requested info + + lpAliasBuffer - not used + + lpdwAliasBufferLength - not used + + hCancellationEvent - the event which signals us to cancel the request + +Return Value: + + The number of CSADDR_INFO structures returned, or SOCKET_ERROR (-1) if + the lpCsAddrBuffer is too small. Use GetLastError() to retrieve the + error code. + +--*/ +{ + DWORD err; + WORD nServiceType; + DWORD cAddress = 0; // Count of the number of address returned + // in lpCsAddrBuffer + DWORD cProtocols = 0; // Count of the number of protocols contained + // in lpdwProtocols + 1 ( for zero terminate ) + DWORD nProt = IPX_BIT | SPXII_BIT; + DWORD fConnectionOriented = (DWORD) -1; + + if ( ARGUMENT_PRESENT( lpdwAliasBufferLength ) + && ARGUMENT_PRESENT( lpAliasBuffer ) + ) + { + if ( *lpdwAliasBufferLength >= sizeof(WCHAR) ) + *lpAliasBuffer = 0; + } + + // + // Check for invalid parameters + // + if ( ( lpServiceType == NULL ) + || ( lpServiceName == NULL ) + || ( lpdwBufferLength == NULL ) + ) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return SOCKET_ERROR; + } + + // + // If an array of protocol ids is passed in, check to see if + // the IPX protocol is requested. If not, return 0 since + // we only support IPX. + // + if ( lpdwProtocols != NULL ) + { + INT i = -1; + + nProt = 0; + while ( lpdwProtocols[++i] != 0 ) + { + if ( lpdwProtocols[i] == NSPROTO_IPX ) + nProt |= IPX_BIT; + + if ( lpdwProtocols[i] == NSPROTO_SPX ) + nProt |= SPX_BIT; + + if ( lpdwProtocols[i] == NSPROTO_SPXII ) + nProt |= SPXII_BIT; + + } + + if ( nProt == 0 ) + return 0; // No address found + + cProtocols = i+1; + } + + // + // Check to see if the service type is supported in NetWare + // + if ( NwpLookupSapInRegistry( lpServiceType, &nServiceType, NULL, + &fConnectionOriented )) + { + if ( fConnectionOriented != -1 ) // Got value from registry + { + if ( fConnectionOriented ) + { + nProt &= ~IPX_BIT; + } + else // connectionless + { + nProt &= ~(SPX_BIT | SPXII_BIT ); + } + + if ( nProt == 0 ) + return 0; // No address found + } + } + else + { + // + // Couldn't find it in the registry, see if it is a well-known GUID + // + if ( IS_SVCID_NETWARE( lpServiceType )) + { + nServiceType = SAPID_FROM_SVCID_NETWARE( lpServiceType ); + } + else + { + // + // Not a well-known GUID either + // + return 0; // No address found + } + } + + + if ((dwResolution & RES_SERVICE) != 0) + { + err = FillBufferWithCsAddr( NULL, + nProt, + lpCsAddrBuffer, + lpdwBufferLength, + &cAddress ); + + if ( err ) + { + SetLastError( err ); + return SOCKET_ERROR; + } + + return cAddress; + } + + // + // Try to get the address from the bindery first + // + RpcTryExcept + { + SOCKADDR_IPX sockaddr; + + err = NwpGetAddressByName( NULL, + nServiceType, + lpServiceName, + &sockaddr ); + + if ( err == NO_ERROR ) + { + err = FillBufferWithCsAddr( sockaddr.sa_netnum, + nProt, + lpCsAddrBuffer, + lpdwBufferLength, + &cAddress ); + } + + } + RpcExcept(1) + { + DWORD code = RpcExceptionCode(); + + if ( code == RPC_S_SERVER_UNAVAILABLE ) + err = ERROR_SERVICE_NOT_ACTIVE; + else + err = NwpMapRpcError( code ); + } + RpcEndExcept + + if ( err + && ( err != ERROR_INSUFFICIENT_BUFFER ) + ) + { + if ( (!(dwResolution & RES_SOFT_SEARCH)) + || ( err == ERROR_SERVICE_NOT_ACTIVE ) + ) + { + // + // We could not find the service name in the bindery, and we + // need to try harder ( RES_SOFT_SEARCH not defined ), so send out + // SAP query packets to see if we can find it. + // + + err = NwpGetAddressViaSap( + nServiceType, + lpServiceName, + nProt, + lpCsAddrBuffer, + lpdwBufferLength, + hCancellationEvent, + &cAddress ); +#if DBG + IF_DEBUG(OTHER) + { + if ( err == NO_ERROR ) + { + KdPrint(("Successfully got %d address for %ws from SAP.\n", + cAddress, lpServiceName )); + } + else + { + KdPrint(("Failed with err %d when getting address for %ws from SAP.\n", err, lpServiceName )); + } + } +#endif + } + else + { + err = NO_ERROR; + cAddress = 0; + } + } + + if ( err ) + { + SetLastError( err ); + return SOCKET_ERROR; + } + + return cAddress; + +} + +DWORD +SapGetService ( + IN LPGUID lpServiceType, + IN LPWSTR lpServiceName, + IN DWORD dwProperties, + IN BOOL fUnicodeBlob, + OUT LPSERVICE_INFO lpServiceInfo, + IN OUT LPDWORD lpdwBufferLen + ) +/*++ + +Routine Description: + + This routine returns the service info for the given service type/name. + +Arguments: + + lpServiceType - pointer to the GUID for the service type + + lpServiceName - service name + + dwProperties - the properties of the service to return + + lpServiceInfo - points to a buffer to return store the return info + + lpdwBufferLen - on input, the count of bytes in lpServiceInfo. On output, + the minimum buffer size that can be passed to this API + to retrieve all the requested information + +Return Value: + + Win32 error code. + +--*/ +{ + DWORD err; + WORD nServiceType; + + // + // Check for invalid parameters + // + if ( ( dwProperties == 0 ) + || ( lpServiceType == NULL ) + || ( lpServiceName == NULL ) + || ( lpServiceName[0] == 0 ) + || ( lpdwBufferLen == NULL ) + ) + { + return ERROR_INVALID_PARAMETER; + } + + // + // Check to see if the service type is supported in NetWare + // + if ( !(NwpLookupSapInRegistry( lpServiceType, &nServiceType, NULL, NULL ))) + { + // + // Couldn't find it in the registry, see if it is a well-known GUID + // + if ( IS_SVCID_NETWARE( lpServiceType )) + { + nServiceType = SAPID_FROM_SVCID_NETWARE( lpServiceType ); + } + else + { + // + // Not a well-known GUID either, return error + // + return ERROR_SERVICE_NOT_FOUND; + } + } + + UNREFERENCED_PARAMETER(fUnicodeBlob) ; + + RpcTryExcept + { + err = NwrGetService( NULL, + nServiceType, + lpServiceName, + dwProperties, + (LPBYTE) lpServiceInfo, + *lpdwBufferLen, + lpdwBufferLen ); + + if ( err == NO_ERROR ) + { + INT i ; + LPSERVICE_INFO p = (LPSERVICE_INFO) lpServiceInfo; + LPSERVICE_ADDRESS lpAddress ; + + // + // fix up pointers n main structure (convert from offsets) + // + if ( p->lpServiceType != NULL ) + p->lpServiceType = (LPGUID) ((DWORD) p->lpServiceType + + (LPBYTE) p); + if ( p->lpServiceName != NULL ) + p->lpServiceName = (LPWSTR) + ((DWORD) p->lpServiceName + (LPBYTE) p); + if ( p->lpComment != NULL ) + p->lpComment = (LPWSTR) ((DWORD) p->lpComment + (LPBYTE) p); + if ( p->lpLocale != NULL ) + p->lpLocale = (LPWSTR) ((DWORD) p->lpLocale + (LPBYTE) p); + if ( p->lpMachineName != NULL ) + p->lpMachineName = (LPWSTR) + ((DWORD) p->lpMachineName + (LPBYTE)p); + if ( p->lpServiceAddress != NULL ) + p->lpServiceAddress = (LPSERVICE_ADDRESSES) + ((DWORD) p->lpServiceAddress + (LPBYTE) p); + if ( p->ServiceSpecificInfo.pBlobData != NULL ) + p->ServiceSpecificInfo.pBlobData = (LPBYTE) + ((DWORD) p->ServiceSpecificInfo.pBlobData + (LPBYTE) p); + + // + // fix up pointers in the array of addresses + // + for (i = p->lpServiceAddress->dwAddressCount; + i > 0; + i--) + { + lpAddress = + &(p->lpServiceAddress->Addresses[i-1]) ; + lpAddress->lpAddress = + ((LPBYTE)p) + (DWORD)lpAddress->lpAddress ; + lpAddress->lpPrincipal = + ((LPBYTE)p) + (DWORD)lpAddress->lpPrincipal ; + } + } + } + RpcExcept(1) + { + err = ERROR_SERVICE_NOT_ACTIVE; +#if 0 // the following is a good idea, but hard to get right + DWORD code = RpcExceptionCode(); + + if ( (code == RPC_S_SERVER_UNAVAILABLE) + || + (code == RPC_S_UNKNOWN_IF) ) + err + err = ERROR_SERVICE_NOT_ACTIVE; + else + err = NwpMapRpcError( code ); +#endif + } + RpcEndExcept + + if ( err == ERROR_SERVICE_NOT_ACTIVE ) + { + // + //CSNW not available, going to get it ourselves + // + err = NwGetService( NULL, + nServiceType, + lpServiceName, + dwProperties, + (LPBYTE) lpServiceInfo, + *lpdwBufferLen, + lpdwBufferLen ); + + if ( err == NO_ERROR ) + { + INT i ; + LPSERVICE_INFO p = (LPSERVICE_INFO) lpServiceInfo; + LPSERVICE_ADDRESS lpAddress ; + + // + // fix up pointers n main structure (convert from offsets) + // + if ( p->lpServiceType != NULL ) + p->lpServiceType = (LPGUID) ((DWORD) p->lpServiceType + + (LPBYTE) p); + if ( p->lpServiceName != NULL ) + p->lpServiceName = (LPWSTR) + ((DWORD) p->lpServiceName + (LPBYTE) p); + if ( p->lpComment != NULL ) + p->lpComment = (LPWSTR) ((DWORD) p->lpComment + (LPBYTE) p); + if ( p->lpLocale != NULL ) + p->lpLocale = (LPWSTR) ((DWORD) p->lpLocale + (LPBYTE) p); + if ( p->lpMachineName != NULL ) + p->lpMachineName = (LPWSTR) + ((DWORD) p->lpMachineName + (LPBYTE)p); + if ( p->lpServiceAddress != NULL ) + p->lpServiceAddress = (LPSERVICE_ADDRESSES) + ((DWORD) p->lpServiceAddress + (LPBYTE) p); + if ( p->ServiceSpecificInfo.pBlobData != NULL ) + p->ServiceSpecificInfo.pBlobData = (LPBYTE) + ((DWORD) p->ServiceSpecificInfo.pBlobData + (LPBYTE) p); + + // + // fix up pointers in the array of addresses + // + for (i = p->lpServiceAddress->dwAddressCount; + i > 0; + i--) + { + lpAddress = + &(p->lpServiceAddress->Addresses[i-1]) ; + lpAddress->lpAddress = + ((LPBYTE)p) + (DWORD)lpAddress->lpAddress ; + lpAddress->lpPrincipal = + ((LPBYTE)p) + (DWORD)lpAddress->lpPrincipal ; + } + } + } + + return err; +} + +DWORD +SapSetService ( + IN DWORD dwOperation, + IN DWORD dwFlags, + IN BOOL fUnicodeBlob, + IN LPSERVICE_INFO lpServiceInfo + ) +/*++ + +Routine Description: + + This routine registers or deregisters the given service type/name. + +Arguments: + + dwOperation - Either SERVICE_REGISTER, SERVICE_DEREGISTER, + SERVICE_ADD_TYPE, SERVICE_DELETE_TYPE, + or SERVICE_FLUSH + + dwFlags - ignored + + lpServiceInfo - Pointer to a SERVICE_INFO structure containing all info + about the service. + +Return Value: + + Win32 error code. + +--*/ +{ + DWORD err; + WORD nServiceType; + + UNREFERENCED_PARAMETER( dwFlags ); + + // + // Check for invalid parameters + // + switch ( dwOperation ) + { + case SERVICE_REGISTER: + case SERVICE_DEREGISTER: + case SERVICE_ADD_TYPE: + case SERVICE_DELETE_TYPE: + break; + + case SERVICE_FLUSH: + // + // This is a no-op in our provider, so just return success + // + return NO_ERROR; + + default: + // + // We can probably say all other operations which we have no + // knowledge of are ignored by us. So, just return success. + // + return NO_ERROR; + } + + if ( ( lpServiceInfo == NULL ) + || ( lpServiceInfo->lpServiceType == NULL ) + || ( ((lpServiceInfo->lpServiceName == NULL) || + (lpServiceInfo->lpServiceName[0] == 0 )) && + ((dwOperation != SERVICE_ADD_TYPE) && + (dwOperation != SERVICE_DELETE_TYPE)) + ) + + ) + { + return ERROR_INVALID_PARAMETER; + } + + // + // See if operation is adding or deleting a service type + // + if ( dwOperation == SERVICE_ADD_TYPE ) + { + return NwpAddServiceType( lpServiceInfo, fUnicodeBlob ); + } + else if ( dwOperation == SERVICE_DELETE_TYPE ) + { + return NwpDeleteServiceType( lpServiceInfo, fUnicodeBlob ); + } + + // + // Check to see if the service type is supported in NetWare + // + if ( !(NwpLookupSapInRegistry( lpServiceInfo->lpServiceType, &nServiceType, NULL, NULL ))) + { + // + // Couldn't find it in the registry, see if it is a well-known GUID + // + if ( IS_SVCID_NETWARE( lpServiceInfo->lpServiceType )) + { + nServiceType = SAPID_FROM_SVCID_NETWARE( lpServiceInfo->lpServiceType ); + } + else + { + // + // Not a well-known GUID either, return error + // + return ERROR_SERVICE_NOT_FOUND; + } + } + + // + // Operation is either SERVICE_REGISTER or SERVICE_DEREGISTER. + // Pass it on to the common code used by this and the RnR2 + // SetService + // + + err = pSapSetService(dwOperation, lpServiceInfo, nServiceType); + return(err); +} + +DWORD +pSapSetService2( + IN DWORD dwOperation, + IN LPWSTR lpszServiceInstance, + IN PBYTE pbAddress, + IN LPGUID pType, + IN WORD nServiceType + ) +/*++ +Routine Description: + Jacket routine called by the RnR2 SetService. This routine is + an impedance matcher to coerce data structures. It winds + up calling pSapSetService2 once it has constructed the + SERVICE_INFO structure. +--*/ +{ + SERVICE_INFO siInfo; + SERVICE_ADDRESSES ServiceAddresses; + LPSERVICE_ADDRESS psa = &ServiceAddresses.Addresses[0]; + + ServiceAddresses.dwAddressCount = 1; + memset(&siInfo, 0, sizeof(siInfo)); + siInfo.lpServiceName = lpszServiceInstance; + siInfo.lpServiceAddress = &ServiceAddresses; + psa->dwAddressType = AF_IPX; + psa->dwAddressFlags = psa->dwPrincipalLength = 0; + psa->dwAddressLength = sizeof(SOCKADDR_IPX); + psa->lpPrincipal = 0; + psa->lpAddress = pbAddress; + siInfo.lpServiceType = pType; + return(pSapSetService(dwOperation, &siInfo, nServiceType)); +} + + +DWORD +pSapSetService( + IN DWORD dwOperation, + IN LPSERVICE_INFO lpServiceInfo, + IN WORD nServiceType) +/*++ +Routine Description: + Common routine to do the SAP advertisement. +--*/ +{ + DWORD err; + + RpcTryExcept + { + err = NwrSetService( NULL, dwOperation, lpServiceInfo, nServiceType ); + } + RpcExcept(1) + { + err = ERROR_SERVICE_NOT_ACTIVE; +#if 0 + DWORD code = RpcExceptionCode(); + + if ( (code == RPC_S_SERVER_UNAVAILABLE) + || + (code == RPC_S_UNKNOWN_IF) ) + { + err = ERROR_SERVICE_NOT_ACTIVE; + } + else + { + err = NwpMapRpcError( code ); + } +#endif + } + RpcEndExcept + + if ( err == ERROR_SERVICE_NOT_ACTIVE ) + { + // + //CSNW not available, going to try use the SAP agent, else we do it ourselves + // + err = NO_ERROR; + + // + // Check if all parameters passed in are valid + // + if ( wcslen( lpServiceInfo->lpServiceName ) > SAP_OBJECT_NAME_MAX_LENGTH-1 ) + { + return ERROR_INVALID_PARAMETER; + } + + switch ( dwOperation ) + { + case SERVICE_REGISTER: + err = NwRegisterService( lpServiceInfo, nServiceType, NULL ); + break; + + case SERVICE_DEREGISTER: + err = NwDeregisterService( lpServiceInfo, nServiceType ); + break; + + default: //this should never occur, but just in case . . . + err = ERROR_INVALID_PARAMETER; + break; + } + } + + return err; +} + +DWORD +SapFreeSapSocket(SOCKET s) +{ +/*++ +Routine Description: + + Release the socket and clean up +--*/ + DWORD err = NO_ERROR; + + closesocket( s ); + return(err); +} + +DWORD +SapGetSapSocket(SOCKET * ps) +{ +/*++ +Routine Description: + + Get a socket suitable for making SAP queries + +Arguments: None + +--*/ + SOCKET socketSap; + WSADATA wsaData; + SOCKADDR_IPX socketAddr; + DWORD err = NO_ERROR; + INT nValue; + DWORD dwNonBlocking = 1; + + // + // Initialize the socket interface + // +// err = WSAStartup( WSOCK_VER_REQD, &wsaData ); +// if ( err ) +// { +// return err; +// } + + // + // Open an IPX datagram socket + // + socketSap = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX ); + if ( socketSap == INVALID_SOCKET ) + { + err = WSAGetLastError(); +// (VOID) WSACleanup(); + return err; + } + + // + // Set the socket to non-blocking + // + if ( ioctlsocket( socketSap, FIONBIO, &dwNonBlocking ) == SOCKET_ERROR ) + { + err = WSAGetLastError(); + goto ErrExit; + } + + // + // Allow sending of broadcasts + // + nValue = 1; + if ( setsockopt( socketSap, + SOL_SOCKET, + SO_BROADCAST, + (PVOID) &nValue, + sizeof(INT)) == SOCKET_ERROR ) + { + err = WSAGetLastError(); + goto ErrExit; + } + + // + // Bind the socket + // + memset( &socketAddr, 0, sizeof( SOCKADDR_IPX)); + socketAddr.sa_family = AF_IPX; + socketAddr.sa_socket = 0; // no specific port + + if ( bind( socketSap, + (PSOCKADDR) &socketAddr, + sizeof( SOCKADDR_IPX)) == SOCKET_ERROR ) + { + err = WSAGetLastError(); + goto ErrExit; + } + + // + // Set the extended address option + // + nValue = 1; + if ( setsockopt( socketSap, // Socket Handle + NSPROTO_IPX, // Option Level + IPX_EXTENDED_ADDRESS, // Option Name + (PUCHAR)&nValue, // Ptr to on/off flag + sizeof(INT)) == SOCKET_ERROR ) // Length of flag + { + err = WSAGetLastError(); + goto ErrExit; + } + + *ps = socketSap; + + return(err); + +ErrExit: + SapFreeSapSocket(socketSap); // cleans up lots of stuff + return(err); +} + + +DWORD +NwpGetAddressForRnRViaSap( + IN HANDLE hRnRHandle, + IN WORD nServiceType, + IN LPWSTR lpServiceName, + IN DWORD nProt, + IN OUT LPVOID lpCsAddrBuffer, + IN OUT LPDWORD lpdwBufferLength, + IN HANDLE hCancellationEvent, + OUT LPDWORD lpcAddress + ) +{ +/*++ +Routine Description: + + This routine uses SAP requests to find the address of the given service + name/type. It can handle looking up by type only, or by name and type. + The latter case is the same as the old RnR code, see below for + it and for a description of the arguments +--*/ + return(0); +} + +#define MAX_LOOPS_FOR_SAP 4 + +DWORD +SapGetSapForType( + PSAP_BCAST_CONTROL psbc, + WORD nServiceType) +{ +/*++ +Routine Description: + Does the work of send Sap queries and fetching results. + The first message sent is done according to the requester, and + may be limited to the local LAN or not. + +Arguments: + psbc -- pointer to the control information + wSapType -- Sap type +--*/ + SAP_REQUEST sapRequest; + UCHAR destAddr[SAP_ADDRESS_LENGTH]; + DWORD startTickCount; + UCHAR recvBuffer[SAP_MAXRECV_LENGTH]; + INT bytesReceived; + BOOL fFound = FALSE; + DWORD err = NO_ERROR; + + sapRequest.QueryType = htons( psbc->wQueryType ); + sapRequest.ServerType = htons( nServiceType ); + + // + // Set the address to send to + // + memcpy( destAddr, SapBroadcastAddress, SAP_ADDRESS_LENGTH ); + + // + // Ready to go. This might be the inital call, in which case + // we start off by sending. In all other cases, we start + // out receiving. + // + + // + // In the full case, + // we will send out SAP requests 3 times and wait 1 sec for + // Sap responses the first time, 2 sec the second and 4 sec the + // third time. + // + for (; !fFound && (psbc->dwIndex < MAX_LOOPS_FOR_SAP); psbc->dwIndex++ ) + { + DWORD dwRet; + DWORD dwTimeOut = (1 << psbc->dwIndex) * 1000; + + if(psbc->dwTickCount) + { + dwRet = dwrcNil; + // + // Need to do some reading ... + // + do + { + PSAP_IDENT_HEADER pSap; + + + if((psbc->psrc->fFlags & SAP_F_END_CALLED) + || + psbc->fCheckCancel(psbc->pvArg)) + { + err = dwrcCancel; + goto CleanExit; + } + + // + // Sleeps for 50 ms so that we might get something on first read + // + Sleep( 50 ); + + bytesReceived = recvfrom( psbc->s, + recvBuffer, + SAP_MAXRECV_LENGTH, + 0, + NULL, + NULL ); + + if ( bytesReceived == SOCKET_ERROR ) + { + err = WSAGetLastError(); + if ( err == WSAEWOULDBLOCK ) // no data on socket, continue looping + { + if(dwRet == dwrcNoWait) + { + fFound = TRUE; + } + err = NO_ERROR; + continue; + } + } + + if ( ( err != NO_ERROR ) // err occurred in recvfrom + || ( bytesReceived == 0 ) // or socket closed + ) + { + goto CleanExit; + } + + // + // Skip over query type + // + bytesReceived -= sizeof(USHORT); + pSap = (PSAP_IDENT_HEADER) &(recvBuffer[sizeof(USHORT)]); + + // + // Tell the caller we've something to look over + // + while ( bytesReceived >= sizeof( SAP_IDENT_HEADER )) + { + + dwRet = psbc->Func(psbc, pSap, &err); + if((dwRet == dwrcDone) + || + (dwRet == dwrcCancel)) + { + fFound = TRUE; + break; + } + + pSap++; + bytesReceived -= sizeof( SAP_IDENT_HEADER ); + } + } + while ( !fFound + && ((GetTickCount() - psbc->dwTickCount) < dwTimeOut ) + ); + } + + + // Send the packet out + // + if((fFound && (dwRet == dwrcNoWait)) + || + (psbc->dwIndex == (MAX_LOOPS_FOR_SAP -1))) + { + goto CleanExit; + } + if ( sendto( psbc->s, + (PVOID) &sapRequest, + sizeof( sapRequest ), + 0, + (PSOCKADDR) destAddr, + SAP_ADDRESS_LENGTH ) == SOCKET_ERROR ) + { + err = WSAGetLastError(); + goto CleanExit; + } + psbc->dwTickCount = GetTickCount(); + } + + if(!fFound) + { + err = WSAEADDRNOTAVAIL; + } + +CleanExit: + + return err; +} + +BOOL +NwpLookupSapInRegistry( + IN LPGUID lpServiceType, + OUT PWORD pnSapType, + OUT PWORD pwPort, + IN OUT PDWORD pfConnectionOriented + ) +/*++ + +Routine Description: + + This routine looks up the GUID in the registry under + Control\ServiceProvider\ServiceTypes and trys to read the SAP type + from the registry. + +Arguments: + + lpServiceType - the GUID to look for + pnSapType - on return, contains the SAP type + +Return Value: + + Returns FALSE if we can't get the SAP type, TRUE otherwise + +--*/ +{ + DWORD err; + BOOL fFound = FALSE; + + HKEY hkey = NULL; + HKEY hkeyServiceType = NULL; + DWORD dwIndex = 0; + WCHAR szBuffer[ MAX_PATH + 1]; + DWORD dwLen; + FILETIME ftLastWrite; + + // + // Open the service types key + // + err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, + NW_SERVICE_TYPES_REGKEY, + 0, + KEY_READ, + &hkey ); + + if ( err ) + { + // Cannot find the key because it is not created yet since no + // one called Add service type. We return FALSE indicating + // Sap type not found. + return FALSE; + } + + // + // Loop through all subkey of service types to find the GUID + // + for ( dwIndex = 0; ; dwIndex++ ) + { + GUID guid; + + dwLen = sizeof( szBuffer ) / sizeof( WCHAR ); + err = RegEnumKeyExW( hkey, + dwIndex, + szBuffer, // Buffer big enough to + // hold any key name + &dwLen, // in characters + NULL, + NULL, + NULL, + &ftLastWrite ); + + // + // We will break out of here on any error, this includes + // the error ERROR_NO_MORE_ITEMS which means that we have finish + // enumerating all the keys. + // + if ( err ) + { + if ( err == ERROR_NO_MORE_ITEMS ) // No more to enumerate + err = NO_ERROR; + break; + } + + err = RegOpenKeyExW( hkey, + szBuffer, + 0, + KEY_READ, + &hkeyServiceType ); + + + if ( err ) + break; + + dwLen = sizeof( szBuffer ); + err = RegQueryValueExW( hkeyServiceType, + NW_GUID_VALUE_NAME, + NULL, + NULL, + (LPBYTE) szBuffer, // Buffer big enough to + // hold any GUID + &dwLen ); // in bytes + + if ( err == ERROR_FILE_NOT_FOUND ) + continue; // continue with the next key + else if ( err ) + break; + + + // Get rid of the end curly brace + szBuffer[ dwLen/sizeof(WCHAR) - 2] = 0; + + err = UuidFromStringW( szBuffer + 1, // go past the first curly brace + &guid ); + + if ( err ) + continue; // continue with the next key, err might be returned + // if buffer does not contain a valid GUID + + if ( !memcmp( lpServiceType, &guid, sizeof(GUID))) + { + DWORD dwTmp; + dwLen = sizeof( dwTmp ); + err = RegQueryValueExW( hkeyServiceType, + SERVICE_TYPE_VALUE_SAPID, + NULL, + NULL, + (LPBYTE) &dwTmp, + &dwLen ); // in bytes + + if ( !err ) + { + fFound = TRUE; + *pnSapType = (WORD) dwTmp; + if ( ARGUMENT_PRESENT( pwPort )) + { + err = RegQueryValueExW( hkeyServiceType, + L"Port", + NULL, + NULL, + (LPBYTE) &dwTmp, + &dwLen ); // in bytes + + if ( !err ) + { + *pwPort = (WORD)dwTmp; + } + } + if ( ARGUMENT_PRESENT( pfConnectionOriented )) + { + err = RegQueryValueExW( hkeyServiceType, + SERVICE_TYPE_VALUE_CONN, + NULL, + NULL, + (LPBYTE) &dwTmp, + &dwLen ); // in bytes + + if ( !err ) + *pfConnectionOriented = dwTmp? 1: 0; + } + } + else if ( err == ERROR_FILE_NOT_FOUND ) + { + continue; // continue with the next key since we can't + // find Sap Id + } + break; + } + + RegCloseKey( hkeyServiceType ); + hkeyServiceType = NULL; + } + + if ( hkeyServiceType != NULL ) + RegCloseKey( hkeyServiceType ); + + if ( hkey != NULL ) + RegCloseKey( hkey ); + + return fFound; +} + +DWORD +NwpRnR2AddServiceType( + IN LPWSTR lpServiceTypeName, + IN LPGUID lpClassType, + IN WORD wSapId, + IN WORD wPort +) +{ + HKEY hKey, hKeyService; + PWCHAR pwszUuid; + DWORD dwDisposition, err; + DWORD dwValue = (DWORD)wSapId; + WCHAR wszUuid[36 + 1 + 2]; // to hold the GUID + + err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, + NW_SERVICE_TYPES_REGKEY, + 0, + TEXT(""), + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, + NULL, + &hKey, + &dwDisposition ); + + if(err) + { + return(GetLastError()); + } + + // + // Open the key corresponding to the service (create if not there). + // + + err = RegCreateKeyEx( + hKey, + lpServiceTypeName, + 0, + TEXT(""), + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, + NULL, + &hKeyService, + &dwDisposition + ); + + if(!err) + { + // + // ready to put the GUID value in. + // + + UuidToString( + lpClassType, + &pwszUuid); + + wszUuid[0] = L'{'; + memcpy(&wszUuid[1], pwszUuid, 36 * sizeof(WCHAR)); + wszUuid[37] = L'}'; + wszUuid[38] = 0; + + RpcStringFree(&pwszUuid); + + // + // write it + // + + err = RegSetValueEx( + hKeyService, + L"GUID", + 0, + REG_SZ, + (LPBYTE)wszUuid, + 39 * sizeof(WCHAR)); + + if(!err) + { + err = RegSetValueEx( + hKeyService, + L"SAPID", + 0, + REG_DWORD, + (LPBYTE)&dwValue, + sizeof(DWORD)); + + dwValue = (DWORD)wPort; + + err = RegSetValueEx( + hKeyService, + L"PORT", + 0, + REG_DWORD, + (LPBYTE)&dwValue, + sizeof(DWORD)); + } + RegCloseKey(hKeyService); + } + RegCloseKey(hKey); + if(err) + { + err = GetLastError(); + } + return(err); +} + + +DWORD +NwpAddServiceType( + IN LPSERVICE_INFO lpServiceInfo, + IN BOOL fUnicodeBlob +) +/*++ + +Routine Description: + + This routine adds a new service type and its info to the registry under + Control\ServiceProvider\ServiceTypes + +Arguments: + + lpServiceInfo - the ServiceSpecificInfo contains the service type info + fUnicodeBlob - TRUE if the above field contains unicode data, + FALSE otherwise + +Return Value: + + Win32 error + +--*/ +{ + DWORD err; + HKEY hkey = NULL; + HKEY hkeyType = NULL; + + SERVICE_TYPE_INFO *pSvcTypeInfo = (SERVICE_TYPE_INFO *) + lpServiceInfo->ServiceSpecificInfo.pBlobData; + LPWSTR pszSvcTypeName; + UNICODE_STRING uniStr; + DWORD i; + PSERVICE_TYPE_VALUE pVal; + + // + // Get the new service type name + // + if ( fUnicodeBlob ) + { + pszSvcTypeName = (LPWSTR) (((LPBYTE) pSvcTypeInfo) + + pSvcTypeInfo->dwTypeNameOffset ); + } + else + { + ANSI_STRING ansiStr; + + RtlInitAnsiString( &ansiStr, + (LPSTR) (((LPBYTE) pSvcTypeInfo) + + pSvcTypeInfo->dwTypeNameOffset )); + + err = RtlAnsiStringToUnicodeString( &uniStr, &ansiStr, TRUE ); + if ( err ) + return err; + + pszSvcTypeName = uniStr.Buffer; + } + + // + // If the service type name is an empty string, return error. + // + if ( ( pSvcTypeInfo->dwTypeNameOffset == 0 ) + || ( pszSvcTypeName == NULL ) + || ( *pszSvcTypeName == 0 ) // empty string + ) + { + err = ERROR_INVALID_PARAMETER; + goto CleanExit; + + } + + // + // The following keys should have already been created + // + err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, + NW_SERVICE_TYPES_REGKEY, + 0, + KEY_READ | KEY_WRITE, + &hkey ); + + if ( err ) + goto CleanExit; + + err = RegOpenKeyExW( hkey, + pszSvcTypeName, + 0, + KEY_READ | KEY_WRITE, + &hkeyType ); + + if ( err ) + goto CleanExit; + + // + // Loop through all values in the specific and add them one by one + // to the registry if it belongs to our name space + // + for ( i = 0, pVal = pSvcTypeInfo->Values; + i < pSvcTypeInfo->dwValueCount; + i++, pVal++ ) + { + if ( ! ((pVal->dwNameSpace == NS_SAP) || + (pVal->dwNameSpace == NS_DEFAULT)) ) + { + continue; // ignore values not in our name space + } + + if ( fUnicodeBlob ) + { + err = RegSetValueExW( + hkeyType, + (LPWSTR) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueNameOffset), + 0, + pVal->dwValueType, + (LPBYTE) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueOffset), + pVal->dwValueSize + ); + } + else + { + err = RegSetValueExA( + hkeyType, + (LPSTR) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueNameOffset), + 0, + pVal->dwValueType, + (LPBYTE) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueOffset), + pVal->dwValueSize + ); + } + } + +CleanExit: + + if ( !fUnicodeBlob ) + RtlFreeUnicodeString( &uniStr ); + + if ( hkeyType != NULL ) + RegCloseKey( hkeyType ); + + if ( hkey != NULL ) + RegCloseKey( hkey ); + + return err; + +} + +DWORD +NwpDeleteServiceType( + IN LPSERVICE_INFO lpServiceInfo, + IN BOOL fUnicodeBlob +) +/*++ + +Routine Description: + + This routine deletes a service type and its info from the registry under + Control\ServiceProvider\ServiceTypes + +Arguments: + + lpServiceInfo - the ServiceSpecificInfo contains the service type info + fUnicodeBlob - TRUE if the above field contains unicode data, + FALSE otherwise + +Return Value: + + Win32 error + +--*/ +{ + DWORD err; + HKEY hkey = NULL; + SERVICE_TYPE_INFO *pSvcTypeInfo = (SERVICE_TYPE_INFO *) + lpServiceInfo->ServiceSpecificInfo.pBlobData; + LPWSTR pszSvcTypeName; + UNICODE_STRING uniStr; + + // + // Get the service type name to be deleted + // + if ( fUnicodeBlob ) + { + pszSvcTypeName = (LPWSTR) (((LPBYTE) pSvcTypeInfo) + + pSvcTypeInfo->dwTypeNameOffset ); + } + else + { + ANSI_STRING ansiStr; + + RtlInitAnsiString( &ansiStr, + (LPSTR) (((LPBYTE) pSvcTypeInfo) + + pSvcTypeInfo->dwTypeNameOffset )); + + err = RtlAnsiStringToUnicodeString( &uniStr, &ansiStr, TRUE ); + if ( err ) + return err; + + pszSvcTypeName = uniStr.Buffer; + } + + // + // If the service type name is an empty string, return error. + // + if ( ( pSvcTypeInfo->dwTypeNameOffset == 0 ) + || ( pszSvcTypeName == NULL ) + || ( *pszSvcTypeName == 0 ) // empty string + ) + { + err = ERROR_INVALID_PARAMETER; + goto CleanExit; + + } + + err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, + NW_SERVICE_TYPES_REGKEY, + 0, + KEY_READ | KEY_WRITE, + &hkey ); + + + if ( !err ) + { + err = RegDeleteKey( hkey, + pszSvcTypeName ); + } + + if ( err == ERROR_FILE_NOT_FOUND ) + { + // Perhaps before calling my provider, the router already deleted the + // this key, hence just return success; + err = NO_ERROR; + } + +CleanExit: + + if ( !fUnicodeBlob ) + RtlFreeUnicodeString( &uniStr ); + + if ( hkey != NULL ) + RegCloseKey( hkey ); + + return err; + +} + +#define SOCKSIZE (sizeof(SOCKADDR_IPX) + sizeof(DWORD) - 1) + +DWORD +FillBufferWithCsAddr( + IN LPBYTE pAddress, + IN DWORD nProt, + IN OUT LPVOID lpCsAddrBuffer, + IN OUT LPDWORD lpdwBufferLength, + OUT LPDWORD pcAddress +) +{ + DWORD nAddrCount = 0; + CSADDR_INFO *pCsAddr; + SOCKADDR_IPX *pAddrLocal, *pAddrRemote; + DWORD i; + LPBYTE pBuffer; + + if ( nProt & SPXII_BIT ) + nAddrCount++; + + if ( nProt & IPX_BIT ) + nAddrCount++; + + if ( nProt & SPX_BIT ) + nAddrCount++; + + + if ( *lpdwBufferLength < + nAddrCount * ( sizeof( CSADDR_INFO) + (2*SOCKSIZE))) + { + *lpdwBufferLength = sizeof(DWORD) -1 + (nAddrCount * + ( sizeof( CSADDR_INFO) + (2 * SOCKSIZE))); + return ERROR_INSUFFICIENT_BUFFER; + } + + pBuffer = ((LPBYTE) lpCsAddrBuffer) + sizeof( CSADDR_INFO) * nAddrCount; + + for ( i = 0, pCsAddr = (CSADDR_INFO *)lpCsAddrBuffer; + (i < nAddrCount) && ( nProt != 0 ); + i++, pCsAddr++ ) + { + if ( nProt & SPXII_BIT ) + { + pCsAddr->iSocketType = SOCK_SEQPACKET; + pCsAddr->iProtocol = NSPROTO_SPXII; + nProt &= ~SPXII_BIT; + } + else if ( nProt & IPX_BIT ) + { + pCsAddr->iSocketType = SOCK_DGRAM; + pCsAddr->iProtocol = NSPROTO_IPX; + nProt &= ~IPX_BIT; + } + else if ( nProt & SPX_BIT ) + { + pCsAddr->iSocketType = SOCK_SEQPACKET; + pCsAddr->iProtocol = NSPROTO_SPX; + nProt &= ~SPX_BIT; + } + else + { + break; + } + + pCsAddr->LocalAddr.iSockaddrLength = sizeof( SOCKADDR_IPX ); + pCsAddr->RemoteAddr.iSockaddrLength = sizeof( SOCKADDR_IPX ); + pCsAddr->LocalAddr.lpSockaddr = + (LPSOCKADDR) pBuffer; + pCsAddr->RemoteAddr.lpSockaddr = + (LPSOCKADDR) ( pBuffer + sizeof(SOCKADDR_IPX)); + pBuffer += 2 * sizeof( SOCKADDR_IPX ); + + pAddrLocal = (SOCKADDR_IPX *) pCsAddr->LocalAddr.lpSockaddr; + pAddrRemote = (SOCKADDR_IPX *) pCsAddr->RemoteAddr.lpSockaddr; + + pAddrLocal->sa_family = AF_IPX; + pAddrRemote->sa_family = AF_IPX; + + // + // The default local sockaddr is for IPX is + // sa_family = AF_IPX and all other bytes = 0. + // + + RtlZeroMemory( pAddrLocal->sa_netnum, + IPX_ADDRESS_LENGTH ); + + // + // If pAddress is NULL, i.e. we are doing RES_SERVICE, + // just make all bytes in remote address zero. + // + if ( pAddress == NULL ) + { + RtlZeroMemory( pAddrRemote->sa_netnum, + IPX_ADDRESS_LENGTH ); + } + else + { + RtlCopyMemory( pAddrRemote->sa_netnum, + pAddress, + IPX_ADDRESS_LENGTH ); + } + } + + *pcAddress = nAddrCount; + + return NO_ERROR; +} + +VOID +NwInitializeServiceProvider( + VOID + ) +/*++ + +Routine Description: + + This routine initializes the service provider. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + // nothing more to do +} + +VOID +NwTerminateServiceProvider( + VOID + ) +/*++ + +Routine Description: + + This routine cleans up the service provider. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + PREGISTERED_SERVICE pSvc, pNext; + + // + // Clean up the link list and stop sending all SAP advertise packets + // + + EnterCriticalSection( &NwServiceListCriticalSection ); + + for ( pSvc = pServiceListHead; pSvc != NULL; pSvc = pNext ) + { + pNext = pSvc->Next; + + if ( pSvc->fAdvertiseBySap ) + { + UNICODE_STRING uServer; + OEM_STRING oemServer; + NTSTATUS ntstatus; + + RtlInitUnicodeString( &uServer, pSvc->pServiceInfo->lpServiceName ); + ntstatus = RtlUnicodeStringToOemString( &oemServer, &uServer, TRUE); + if ( NT_SUCCESS( ntstatus ) ) + { + (VOID) SapRemoveAdvertise( oemServer.Buffer, + pSvc->nSapType ); + RtlFreeOemString( &oemServer ); + } + } + + (VOID) LocalFree( pSvc->pServiceInfo ); + (VOID) LocalFree( pSvc ); + } + + LeaveCriticalSection( &NwServiceListCriticalSection ); + + // + // Clean up the SAP interface + // + (VOID) SapLibShutdown(); + + // + // Clean up the socket interface + // + if ( fInitSocket ) + { + closesocket( socketSap ); +// (VOID) WSACleanup(); + } + +} + +DWORD +NwRegisterService( + IN LPSERVICE_INFO lpServiceInfo, + IN WORD nSapType, + IN HANDLE hEventHandle + ) +/*++ + +Routine Description: + + This routine registers the given service. + +Arguments: + + lpServiceInfo - contains the service information + + nSapType - The SAP type to advertise + + hEventHandle - A handle to the NwDoneEvent if this code is running in + the context of Client Services for NetWare. If this is NULL, + then CSNW is not available and this code is running in the + context of a regular executable. + +Return Value: + + Win32 error. + +--*/ +{ + DWORD err = NO_ERROR; + NTSTATUS ntstatus; + DWORD i; + INT nIPX = -1; + + // + // Check to see if the service address array contains IPX address, + // we will only use the first ipx address contained in the array. + // + if ( lpServiceInfo->lpServiceAddress == NULL ) + return ERROR_INCORRECT_ADDRESS; + + for ( i = 0; i < lpServiceInfo->lpServiceAddress->dwAddressCount; i++) + { + if ( lpServiceInfo->lpServiceAddress->Addresses[i].dwAddressType + == AF_IPX ) + { + nIPX = (INT) i; + break; + } + } + + // + // If we cannot find a IPX address, return error + // + if ( nIPX == -1 ) + return ERROR_INCORRECT_ADDRESS; + + // + // Try to deregister the service since the service might have + // been registered but not deregistered + // + err = NwDeregisterService( lpServiceInfo, nSapType ); + if ( ( err != NO_ERROR ) // deregister successfully + && ( err != ERROR_SERVICE_NOT_FOUND ) // service not registered before + ) + { + return err; + } + + err = NO_ERROR; + + // + // Try and see if SAP service can advertise the service for us. + // + ntstatus = SapLibInit(); + if ( NT_SUCCESS( ntstatus )) + { + UNICODE_STRING uServer; + OEM_STRING oemServer; + INT sapRet; + BOOL fContinueLoop = FALSE; + + RtlInitUnicodeString( &uServer, lpServiceInfo->lpServiceName ); + ntstatus = RtlUnicodeStringToOemString( &oemServer, &uServer, TRUE ); + if ( !NT_SUCCESS( ntstatus )) + return RtlNtStatusToDosError( ntstatus ); + + + do + { + sapRet = SapAddAdvertise( oemServer.Buffer, + nSapType, + (LPBYTE) (((LPSOCKADDR_IPX) lpServiceInfo->lpServiceAddress->Addresses[nIPX].lpAddress)->sa_netnum), + FALSE ); + + switch ( sapRet ) + { + case SAPRETURN_SUCCESS: + { + err = AddServiceToList( lpServiceInfo, nSapType, TRUE, nIPX ); + if ( err ) + (VOID) SapRemoveAdvertise( oemServer.Buffer, nSapType ); + RtlFreeOemString( &oemServer ); + + return err; + } + + case SAPRETURN_NOMEMORY: + err = ERROR_NOT_ENOUGH_MEMORY; + break; + + case SAPRETURN_EXISTS: + { + // + // Someone else is already advertising the service + // directly through SAP service. Remove it and + // readvertise with the new information. + // + sapRet = SapRemoveAdvertise( oemServer.Buffer, nSapType ); + switch ( sapRet ) + { + case SAPRETURN_SUCCESS: + fContinueLoop = TRUE; // go thru once more + break; + + case SAPRETURN_NOMEMORY: + err = ERROR_NOT_ENOUGH_MEMORY; + break; + + case SAPRETURN_NOTEXIST: + case SAPRETURN_INVALIDNAME: + default: // Should not have any other errors + err = ERROR_INVALID_PARAMETER; + break; + } + break; + } + + case SAPRETURN_INVALIDNAME: + err = ERROR_INVALID_PARAMETER; + break; + + case SAPRETURN_DUPLICATE: + err = NO_ERROR; + break; + + default: + break; + } + } while ( fContinueLoop ); + + RtlFreeOemString( &oemServer ); + + if ( err ) + { + return err; + } + } + + // + // At this point, we failed to ask Sap service to advertise the + // service for us. So we advertise it ourselves. + // + + if ( !fInitSocket ) + { + err = NwInitializeSocket( hEventHandle ); + } + + if ( err == NO_ERROR ) + { + err = NwAdvertiseService( lpServiceInfo->lpServiceName, + nSapType, + ((LPSOCKADDR_IPX) lpServiceInfo->lpServiceAddress->Addresses[nIPX].lpAddress), + hEventHandle ); + + // + // Adding the service to the list will result in a resend + // of advertising packets every 60 seconds + // + + if ( err == NO_ERROR ) + { + err = AddServiceToList( lpServiceInfo, nSapType, FALSE, nIPX ); + } + } + + return err; +} + +DWORD +NwDeregisterService( + IN LPSERVICE_INFO lpServiceInfo, + IN WORD nSapType + ) +/*++ + +Routine Description: + + This routine deregisters the given service. + +Arguments: + + lpServiceInfo - contains the service information + + nSapType - SAP type to deregister + +Return Value: + + Win32 error. + +--*/ +{ + PREGISTERED_SERVICE pSvc; + + // + // Check if the requested service type and name has already been registered. + // If yes, then return error. + // + + pSvc = GetServiceItemFromList( nSapType, lpServiceInfo->lpServiceName ); + if ( pSvc == NULL ) + return ERROR_SERVICE_NOT_FOUND; + + // + // If SAP service is advertising the service for us, ask + // the SAP service to stop advertising. + // + + if ( pSvc->fAdvertiseBySap ) + { + UNICODE_STRING uServer; + OEM_STRING oemServer; + NTSTATUS ntstatus; + INT sapRet; + + RtlInitUnicodeString( &uServer, lpServiceInfo->lpServiceName ); + ntstatus = RtlUnicodeStringToOemString( &oemServer, &uServer, TRUE ); + if ( !NT_SUCCESS( ntstatus ) ) + return RtlNtStatusToDosError( ntstatus ); + + sapRet = SapRemoveAdvertise( oemServer.Buffer, nSapType ); + RtlFreeOemString( &oemServer ); + + switch ( sapRet ) + { + case SAPRETURN_NOMEMORY: + return ERROR_NOT_ENOUGH_MEMORY; + + case SAPRETURN_NOTEXIST: + case SAPRETURN_INVALIDNAME: + return ERROR_INVALID_PARAMETER; + + case SAPRETURN_SUCCESS: + break; + + // Should not have any other errors + default: + break; + } + + } + + // + // Remove the service item from the link list + // + RemoveServiceFromList( pSvc ); + + return NO_ERROR; +} + +BOOL +OldRnRCheckCancel( + PVOID pvArg + ) +/*++ +Routine Description: + Determine if the cancel event is signaled +--*/ +{ + POLDRNRSAP porns = (POLDRNRSAP)pvArg; + + if(!WaitForSingleObject(porns->hCancel, 0)) + { + return(TRUE); + } + return(FALSE); +} + + +DWORD +OldRnRCheckSapData( + PSAP_BCAST_CONTROL psbc, + PSAP_IDENT_HEADER pSap, + PDWORD pdwErr + ) +{ +/*++ +Routine Description: + Coroutine called when a SAP reply is recevied. This checks to see + if the reply satisfies the request. +Argument: + pvArg -- actually a pointer to an SAP_BCAST_CONTROL +--*/ + POLDRNRSAP porns = (POLDRNRSAP)psbc->pvArg; + + if(strcmp(porns->poem->Buffer, pSap->ServerName) == 0) + { + // + // it matches. We are done! + // + + *pdwErr = FillBufferWithCsAddr(pSap->Address, + porns->nProt, + porns->lpCsAddrBuffer, + porns->lpdwBufferLength, + porns->lpcAddress); + return(dwrcDone); + } + return(dwrcNil); +} + + + +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 + ) +/*++ + +Routine Description: + + This routine uses SAP requests to find the address of the given service + name/type. It can handle looking up by name and type alone. + +Arguments: + + Handle - the RnR handle, if appropriate + + nServiceType - service type + + lpServiceName - unique string representing the service name + + lpCsAddrBuffer - on return, will be filled with CSADDR_INFO structures + + lpdwBufferLength - on input, the number of bytes contained in the buffer + pointed to by lpCsAddrBuffer. On output, the minimum number of bytes + to pass for the lpCsAddrBuffer to retrieve all the requested info + + hCancellationEvent - the event which signals us to cancel the request + + lpcAddress - on output, the number of CSADDR_INFO structures returned + +Return Value: + + Win32 error code. + +--*/ +{ + DWORD err = NO_ERROR; + NTSTATUS ntstatus; + UNICODE_STRING UServiceName; + OEM_STRING OemServiceName; + SOCKET socketSap; + SAP_RNR_CONTEXT src; + PSAP_BCAST_CONTROL psbc = &src.u_type.sbc; + OLDRNRSAP ors; + + *lpcAddress = 0; + + _wcsupr( lpServiceName ); + RtlInitUnicodeString( &UServiceName, lpServiceName ); + ntstatus = RtlUnicodeStringToOemString( &OemServiceName, + &UServiceName, + TRUE ); + if ( !NT_SUCCESS( ntstatus )) + return RtlNtStatusToDosError( ntstatus ); + + memset(&src, 0, sizeof(src)); + + err = SapGetSapSocket(&psbc->s); + if ( err ) + { + RtlFreeOemString( &OemServiceName ); + return err; + } + + psbc->psrc = &src; + psbc->dwIndex = 0; + psbc->dwTickCount = 0; + psbc->pvArg = (PVOID)&ors; + psbc->Func = OldRnRCheckSapData; + psbc->fCheckCancel = OldRnRCheckCancel; + psbc->fFlags = 0; + psbc->wQueryType = QT_GENERAL_QUERY; + + + + ors.poem = &OemServiceName; + ors.hCancel = hCancellationEvent, + ors.lpCsAddrBuffer = lpCsAddrBuffer; + ors.lpdwBufferLength = lpdwBufferLength; + ors.lpcAddress = lpcAddress; + ors.nProt = nProt; + + err = SapGetSapForType(psbc, nServiceType); + + RtlFreeOemString( &OemServiceName ); + + // + // Clean up the socket interface + // + (VOID)SapFreeSapSocket(psbc->s); + + return err; +} + + + +DWORD +NwGetService( + IN LPWSTR Reserved, + IN WORD nSapType, + IN LPWSTR lpServiceName, + IN DWORD dwProperties, + OUT LPBYTE lpServiceInfo, + IN DWORD dwBufferLength, + OUT LPDWORD lpdwBytesNeeded + ) +/*++ +Routine Description: + + This routine gets the service info. + +Arguments: + + Reserved - unused + + nSapType - SAP type + + lpServiceName - service name + + dwProperties - specifys the properties of the service info needed + + lpServiceInfo - on output, contains the SERVICE_INFO + + dwBufferLength - size of buffer pointed by lpServiceInfo + + lpdwBytesNeeded - if the buffer pointed by lpServiceInfo is not large + enough, this will contain the bytes needed on output + +Return Value: + + Win32 error. + +--*/ +{ + DWORD err = NO_ERROR; + DWORD nSize = sizeof(SERVICE_INFO); + PREGISTERED_SERVICE pSvc; + PSERVICE_INFO pSvcInfo = (PSERVICE_INFO) lpServiceInfo; + LPBYTE pBufferStart; + + UNREFERENCED_PARAMETER( Reserved ); + + // + // Check if all parameters passed in are valid + // + if ( wcslen( lpServiceName ) > SAP_OBJECT_NAME_MAX_LENGTH-1 ) + return ERROR_INVALID_PARAMETER; + + pSvc = GetServiceItemFromList( nSapType, lpServiceName ); + if ( pSvc == NULL ) + return ERROR_SERVICE_NOT_FOUND; + + // + // Calculate the size needed to return the requested info + // + if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_COMMENT )) + && ( pSvc->pServiceInfo->lpComment != NULL ) + ) + { + nSize += ( wcslen( pSvc->pServiceInfo->lpComment) + 1) * sizeof(WCHAR); + } + + if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_LOCALE )) + && ( pSvc->pServiceInfo->lpLocale != NULL ) + ) + { + nSize += ( wcslen( pSvc->pServiceInfo->lpLocale) + 1) * sizeof(WCHAR); + } + + if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_MACHINE )) + && ( pSvc->pServiceInfo->lpMachineName != NULL ) + ) + { + nSize += ( wcslen( pSvc->pServiceInfo->lpMachineName) + 1) * sizeof(WCHAR); + } + + if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_ADDRESSES )) + { + DWORD i; + DWORD dwCount = pSvc->pServiceInfo->lpServiceAddress->dwAddressCount; + + nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD ); + nSize += sizeof( SERVICE_ADDRESSES ); + if ( dwCount > 1 ) + nSize += ( dwCount - 1 ) * sizeof( SERVICE_ADDRESS ); + + for ( i = 0; i < dwCount; i++ ) + { + SERVICE_ADDRESS *pAddr = + &(pSvc->pServiceInfo->lpServiceAddress->Addresses[i]); + + + nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD ); + nSize += pAddr->dwAddressLength; + nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD ); + nSize += pAddr->dwPrincipalLength; + } + } + + if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_SD )) + { + nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD ); + nSize += pSvc->pServiceInfo->ServiceSpecificInfo.cbSize; + } + + // + // Return error if the buffer passed in is not big enough + // + if ( dwBufferLength < nSize ) + { + *lpdwBytesNeeded = nSize; + return ERROR_INSUFFICIENT_BUFFER; + } + + // + // Fill in all requested service info + // + memset( pSvcInfo, 0, sizeof(*pSvcInfo)); // Make all fields 0 i.e. + // all pointer fields NULL + + pSvcInfo->dwDisplayHint = pSvc->pServiceInfo->dwDisplayHint; + pSvcInfo->dwVersion = pSvc->pServiceInfo->dwVersion; + pSvcInfo->dwTime = pSvc->pServiceInfo->dwTime; + + pBufferStart = ((LPBYTE) pSvcInfo) + sizeof( *pSvcInfo ); + + if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_COMMENT )) + && ( pSvc->pServiceInfo->lpComment != NULL ) + ) + { + pSvcInfo->lpComment = (LPWSTR) pBufferStart; + wcscpy( pSvcInfo->lpComment, pSvc->pServiceInfo->lpComment ); + pBufferStart += ( wcslen( pSvcInfo->lpComment ) + 1) * sizeof(WCHAR); + + pSvcInfo->lpComment = (LPWSTR) ((LPBYTE) pSvcInfo->lpComment - lpServiceInfo ); + } + + if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_LOCALE )) + && ( pSvc->pServiceInfo->lpLocale != NULL ) + ) + { + pSvcInfo->lpLocale = (LPWSTR) pBufferStart; + wcscpy( pSvcInfo->lpLocale, pSvc->pServiceInfo->lpLocale ); + pBufferStart += ( wcslen( pSvcInfo->lpLocale ) + 1) * sizeof(WCHAR); + pSvcInfo->lpLocale = (LPWSTR) ((LPBYTE) pSvcInfo->lpLocale - lpServiceInfo); + } + + if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_MACHINE )) + && ( pSvc->pServiceInfo->lpMachineName != NULL ) + ) + { + pSvcInfo->lpMachineName = (LPWSTR) pBufferStart; + wcscpy( pSvcInfo->lpMachineName, pSvc->pServiceInfo->lpMachineName ); + pBufferStart += ( wcslen( pSvcInfo->lpMachineName) + 1) * sizeof(WCHAR); + pSvcInfo->lpMachineName = (LPWSTR) ((LPBYTE) pSvcInfo->lpMachineName - + lpServiceInfo ); + } + + if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_ADDRESSES )) + { + DWORD i, dwCount, dwLen; + + pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD ); + pSvcInfo->lpServiceAddress = (LPSERVICE_ADDRESSES) pBufferStart; + dwCount = pSvcInfo->lpServiceAddress->dwAddressCount = + pSvc->pServiceInfo->lpServiceAddress->dwAddressCount; + + pBufferStart += sizeof( SERVICE_ADDRESSES ); + + for ( i = 0; i < dwCount; i++ ) + { + SERVICE_ADDRESS *pTmpAddr = + &( pSvcInfo->lpServiceAddress->Addresses[i]); + + SERVICE_ADDRESS *pAddr = + &( pSvc->pServiceInfo->lpServiceAddress->Addresses[i]); + + pTmpAddr->dwAddressType = pAddr->dwAddressType; + pTmpAddr->dwAddressFlags = pAddr->dwAddressFlags; + + // + // setup Address + // + pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD ); + pTmpAddr->lpAddress = (LPBYTE) ( pBufferStart - lpServiceInfo ); + pTmpAddr->dwAddressLength = pAddr->dwAddressLength; + memcpy( pBufferStart, pAddr->lpAddress, pAddr->dwAddressLength ); + pBufferStart += pAddr->dwAddressLength; + + // + // setup Principal + // + pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD ); + pTmpAddr->lpPrincipal = (LPBYTE) ( pBufferStart - lpServiceInfo ); + pTmpAddr->dwPrincipalLength = pAddr->dwPrincipalLength; + memcpy(pBufferStart, pAddr->lpPrincipal, pAddr->dwPrincipalLength ); + pBufferStart += pAddr->dwPrincipalLength; + } + + pSvcInfo->lpServiceAddress = (LPSERVICE_ADDRESSES) + ((LPBYTE) pSvcInfo->lpServiceAddress - lpServiceInfo); + } + + if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_SD )) + { + pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD ); + pSvcInfo->ServiceSpecificInfo.cbSize = + pSvc->pServiceInfo->ServiceSpecificInfo.cbSize; + pSvcInfo->ServiceSpecificInfo.pBlobData = pBufferStart; + RtlCopyMemory( pSvcInfo->ServiceSpecificInfo.pBlobData, + pSvc->pServiceInfo->ServiceSpecificInfo.pBlobData, + pSvcInfo->ServiceSpecificInfo.cbSize ); + pSvcInfo->ServiceSpecificInfo.pBlobData = + (LPBYTE) ( pSvcInfo->ServiceSpecificInfo.pBlobData - lpServiceInfo); + } + + return NO_ERROR; +} + +DWORD +NwInitializeSocket( + IN HANDLE hEventHandle + ) +/*++ + +Routine Description: + + This routine initializes the socket needed for us to do the + SAP advertise ourselves. + +Arguments: + + hEventHandle - A handle to the NwDoneEvent if this code is running in + the context of a service. Otherwise this code is running + in the context of a regular executable. + +Return Value: + + Win32 error. + +--*/ +{ + DWORD err = NO_ERROR; + WSADATA wsaData; + SOCKADDR_IPX socketAddr; + INT nValue; + HANDLE hThread; + DWORD dwThreadId; + + if ( fInitSocket ) + return NO_ERROR; + + // + // Initialize the socket interface + // +// err = WSAStartup( WSOCK_VER_REQD, &wsaData ); +// if ( err ) +// return err; + + // + // Open an IPX datagram socket + // + socketSap = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX ); + if ( socketSap == INVALID_SOCKET ) + return WSAGetLastError(); + + // + // Allow sending of broadcasts + // + nValue = 1; + if ( setsockopt( socketSap, + SOL_SOCKET, + SO_BROADCAST, + (PVOID) &nValue, + sizeof(INT)) == SOCKET_ERROR ) + { + err = WSAGetLastError(); + goto CleanExit; + } + + // + // Bind the socket + // + memset( &socketAddr, 0, sizeof( SOCKADDR_IPX)); + socketAddr.sa_family = AF_IPX; + socketAddr.sa_socket = 0; // no specific port + + if ( bind( socketSap, + (PSOCKADDR) &socketAddr, + sizeof( SOCKADDR_IPX)) == SOCKET_ERROR ) + { + err = WSAGetLastError(); + goto CleanExit; + } + + // + // Set the extended address option + // + nValue = 1; + if ( setsockopt( socketSap, // Socket Handle + NSPROTO_IPX, // Option Level + IPX_EXTENDED_ADDRESS, // Option Name + (PUCHAR)&nValue, // Ptr to on/off flag + sizeof(INT)) == SOCKET_ERROR ) // Length of flag + { + + err = WSAGetLastError(); + goto CleanExit; + } + + // + // Create the thread that loops through the registered service + // link list and send out SAP advertise packets for each one of them + // + + hThread = CreateThread( NULL, // no security attributes + 0, // default stack size + SapFunc, // thread function + hEventHandle, // argument to SapFunc + 0, // default creation flags + &dwThreadId ); + + if ( hThread == NULL ) + { + err = GetLastError(); + goto CleanExit; + } + + fInitSocket = TRUE; + +CleanExit: + + if ( err ) + closesocket( socketSap ); + + return err; +} + +DWORD +NwAdvertiseService( + IN LPWSTR lpServiceName, + IN WORD nSapType, + IN LPSOCKADDR_IPX pAddr, + IN HANDLE hEventHandle + ) +/*++ + +Routine Description: + + This routine sends out SAP identification packets for the + given service name and type. + +Arguments: + + lpServiceName - unique string representing the service name + + nSapType - SAP type + + pAddr - address of the service + + hEventHandle - A handle to the NwDoneEvent if this code is running in + the context of a service. Otherwise this code is running + in the context of a regular executable. + +Return Value: + + Win32 error. + +--*/ +{ + NTSTATUS ntstatus; + + UNICODE_STRING uServiceName; + OEM_STRING oemServiceName; + + SAP_IDENT_HEADER_EX sapIdent; + UCHAR destAddr[SAP_ADDRESS_LENGTH]; + PSOCKADDR_IPX pAddrTmp = pAddr; + SOCKADDR_IPX newAddr; + SOCKADDR_IPX bindAddr; + DWORD len = sizeof( SOCKADDR_IPX ); + DWORD getsockname_rc ; + + if ( !fInitSocket ) + { + DWORD err = NwInitializeSocket( hEventHandle ); + if ( err ) + return err; + } + + // + // get local addressing info. we are only interested in the net number. + // + getsockname_rc = getsockname( socketSap, + (PSOCKADDR) &bindAddr, + &len ); + + // + // Convert the service name to OEM string + // + RtlInitUnicodeString( &uServiceName, lpServiceName ); + ntstatus = RtlUnicodeStringToOemString( &oemServiceName, + &uServiceName, + TRUE ); + if ( !NT_SUCCESS( ntstatus )) + return RtlNtStatusToDosError( ntstatus ); + + _strupr( (LPSTR) oemServiceName.Buffer ); + + if ( !memcmp( pAddr->sa_netnum, + "\x00\x00\x00\x00", + IPX_ADDRESS_NETNUM_LENGTH )) + { + if ( getsockname_rc != SOCKET_ERROR ) + { + // copy the ipx address to advertise + memcpy( &newAddr, + pAddr, + sizeof( SOCKADDR_IPX)); + + // replace the net number with the correct one + memcpy( &(newAddr.sa_netnum), + &(bindAddr.sa_netnum), + IPX_ADDRESS_NETNUM_LENGTH ); + + pAddrTmp = &newAddr; + } + } + + // + // Format the SAP identification packet + // + + sapIdent.ResponseType = htons( 2 ); + sapIdent.ServerType = htons( nSapType ); + memset( sapIdent.ServerName, '\0', SAP_OBJECT_NAME_MAX_LENGTH ); + strcpy( sapIdent.ServerName, oemServiceName.Buffer ); + RtlCopyMemory( sapIdent.Address, pAddrTmp->sa_netnum, IPX_ADDRESS_LENGTH ); + sapIdent.HopCount = htons( 1 ); + + RtlFreeOemString( &oemServiceName ); + + // + // Set the address to send to + // + memcpy( destAddr, SapBroadcastAddress, SAP_ADDRESS_LENGTH ); + if ( getsockname_rc != SOCKET_ERROR ) + { + LPSOCKADDR_IPX newDestAddr = (LPSOCKADDR_IPX)destAddr ; + + // + // replace the net number with the correct one + // + memcpy( &(newDestAddr->sa_netnum), + &(bindAddr.sa_netnum), + IPX_ADDRESS_NETNUM_LENGTH ); + + } + + // + // Send the packet out + // + if ( sendto( socketSap, + (PVOID) &sapIdent, + sizeof( sapIdent ), + 0, + (PSOCKADDR) destAddr, + SAP_ADDRESS_LENGTH ) == SOCKET_ERROR ) + { + return WSAGetLastError(); + } + + return NO_ERROR; +} + +DWORD +AddServiceToList( + IN LPSERVICE_INFO lpServiceInfo, + IN WORD nSapType, + IN BOOL fAdvertiseBySap, + IN INT nIndexIPXAddress + ) +/*++ + +Routine Description: + + This routine adds the service to the link list of services + we advertised. + +Arguments: + + lpServiceInfo - service information + + nSapType - SAP type + + fAdvertiseBySap - TRUE if this service is advertised by SAP service, + FALSE if we are advertising ourselves. + + nIndexIPXAddress - index of the ipx address + +Return Value: + + Win32 error. + +--*/ +{ + PREGISTERED_SERVICE pSvcNew; + PSERVICE_INFO pSI; + LPBYTE pBufferStart; + DWORD nSize = 0; + + // + // Allocate a new entry for the service list + // + + pSvcNew = LocalAlloc( LMEM_ZEROINIT, sizeof( REGISTERED_SERVICE )); + if ( pSvcNew == NULL ) + return ERROR_NOT_ENOUGH_MEMORY; + + // + // Calculate the size needed for the SERVICE_INFO structure + // + nSize = sizeof( *lpServiceInfo) + + sizeof( *(lpServiceInfo->lpServiceType)); + + if ( lpServiceInfo->lpServiceName != NULL ) + nSize += ( wcslen( lpServiceInfo->lpServiceName) + 1) * sizeof(WCHAR); + if ( lpServiceInfo->lpComment != NULL ) + nSize += ( wcslen( lpServiceInfo->lpComment) + 1) * sizeof(WCHAR); + if ( lpServiceInfo->lpLocale != NULL ) + nSize += ( wcslen( lpServiceInfo->lpLocale) + 1) * sizeof(WCHAR); + if ( lpServiceInfo->lpMachineName != NULL ) + nSize += ( wcslen( lpServiceInfo->lpMachineName) + 1) * sizeof(WCHAR); + + nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD ); + + if ( lpServiceInfo->lpServiceAddress != NULL ) + { + nSize += sizeof( SERVICE_ADDRESSES ); + nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD ); + + nSize += lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].dwAddressLength; + nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD ); + + nSize += lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].dwPrincipalLength; + nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD ); + } + + nSize += lpServiceInfo->ServiceSpecificInfo.cbSize ; + + // + // Allocate a SERVICE_INFO structure for the new list entry + // + pSI = LocalAlloc( LMEM_ZEROINIT, nSize ); + if ( pSI == NULL ) + { + LocalFree( pSvcNew ); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // + // Copy the information of SERVICE_INFO into list entry + // + *pSI = *lpServiceInfo; + + pBufferStart = (( (LPBYTE) pSI) + sizeof( *lpServiceInfo )); + + pSI->lpServiceType = (LPGUID) pBufferStart; + *(pSI->lpServiceType) = *(lpServiceInfo->lpServiceType); + pBufferStart += sizeof( *(lpServiceInfo->lpServiceType) ); + + if ( lpServiceInfo->lpServiceName != NULL ) + { + pSI->lpServiceName = (LPWSTR) pBufferStart; + wcscpy( pSI->lpServiceName, lpServiceInfo->lpServiceName ); + _wcsupr( pSI->lpServiceName ); + pBufferStart += ( wcslen( lpServiceInfo->lpServiceName ) + 1 ) + * sizeof(WCHAR); + } + + if ( lpServiceInfo->lpComment != NULL ) + { + pSI->lpComment = (LPWSTR) pBufferStart; + wcscpy( pSI->lpComment, lpServiceInfo->lpComment ); + pBufferStart += ( wcslen( lpServiceInfo->lpComment ) + 1 ) + * sizeof(WCHAR); + } + + if ( lpServiceInfo->lpLocale != NULL ) + { + pSI->lpLocale = (LPWSTR) pBufferStart; + wcscpy( pSI->lpLocale, lpServiceInfo->lpLocale ); + pBufferStart += ( wcslen( lpServiceInfo->lpLocale ) + 1 ) + * sizeof(WCHAR); + } + + if ( lpServiceInfo->lpMachineName != NULL ) + { + pSI->lpMachineName = (LPWSTR) pBufferStart; + wcscpy( pSI->lpMachineName, lpServiceInfo->lpMachineName ); + pBufferStart += (wcslen( lpServiceInfo->lpMachineName ) + 1) + * sizeof(WCHAR); + } + + pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ; + + if ( lpServiceInfo->lpServiceAddress != NULL ) + { + DWORD nSize; + + pSI->lpServiceAddress = (LPSERVICE_ADDRESSES) pBufferStart; + pSI->lpServiceAddress->dwAddressCount = 1; // Just 1 IPX address + + memcpy( &(pSI->lpServiceAddress->Addresses[0]), + &(lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress]), + sizeof( SERVICE_ADDRESS) ); + pBufferStart += sizeof( SERVICE_ADDRESSES); + + pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ; + nSize = pSI->lpServiceAddress->Addresses[0].dwAddressLength; + pSI->lpServiceAddress->Addresses[0].lpAddress = pBufferStart; + memcpy( pBufferStart, + lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].lpAddress, + nSize ); + pBufferStart += nSize; + + pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ; + nSize = pSI->lpServiceAddress->Addresses[0].dwPrincipalLength; + pSI->lpServiceAddress->Addresses[0].lpPrincipal = pBufferStart; + memcpy( pBufferStart, + lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].lpPrincipal, + nSize ); + pBufferStart += nSize; + pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ; + } + + pSI->ServiceSpecificInfo.pBlobData = pBufferStart; + RtlCopyMemory( pSI->ServiceSpecificInfo.pBlobData, + lpServiceInfo->ServiceSpecificInfo.pBlobData, + pSI->ServiceSpecificInfo.cbSize ); + + // + // Fill in the data in the list entry + // + pSvcNew->nSapType = nSapType; + pSvcNew->fAdvertiseBySap = fAdvertiseBySap; + pSvcNew->Next = NULL; + pSvcNew->pServiceInfo = pSI; + + // + // Add the newly created list entry into the service list + // + EnterCriticalSection( &NwServiceListCriticalSection ); + + if ( pServiceListHead == NULL ) + pServiceListHead = pSvcNew; + else + pServiceListTail->Next = pSvcNew; + + pServiceListTail = pSvcNew; + + LeaveCriticalSection( &NwServiceListCriticalSection ); + + return NO_ERROR; +} + +VOID +RemoveServiceFromList( + PREGISTERED_SERVICE pSvc + ) +/*++ + +Routine Description: + + This routine removes the service from the link list of services + we advertised. + +Arguments: + + pSvc - the registered service node to remove + +Return Value: + + None. + +--*/ +{ + PREGISTERED_SERVICE pCur, pPrev; + + EnterCriticalSection( &NwServiceListCriticalSection ); + + for ( pCur = pServiceListHead, pPrev = NULL ; pCur != NULL; + pPrev = pCur, pCur = pCur->Next ) + { + if ( pCur == pSvc ) + { + if ( pPrev == NULL ) // i.e. pCur == pSvc == pServiceListHead + { + pServiceListHead = pSvc->Next; + if ( pServiceListTail == pSvc ) + pServiceListTail = NULL; + } + else + { + pPrev->Next = pSvc->Next; + if ( pServiceListTail == pSvc ) + pServiceListTail = pPrev; + } + + (VOID) LocalFree( pCur->pServiceInfo ); + (VOID) LocalFree( pCur ); + break; + } + } + + LeaveCriticalSection( &NwServiceListCriticalSection ); +} + +PREGISTERED_SERVICE +GetServiceItemFromList( + IN WORD nSapType, + IN LPWSTR pServiceName + ) +/*++ + +Routine Description: + + This routine returns the registered service node with the given + service name and type. + +Arguments: + + nSapType - SAP type + + pServiceName - service name + +Return Value: + + Returns the pointer to the registered service node, + NULL if we cannot find the service type/name. + +--*/ +{ + PREGISTERED_SERVICE pSvc; + + EnterCriticalSection( &NwServiceListCriticalSection ); + + for ( pSvc = pServiceListHead; pSvc != NULL; pSvc = pSvc->Next ) + { + if ( ( pSvc->nSapType == nSapType ) + && ( _wcsicmp( pSvc->pServiceInfo->lpServiceName, pServiceName ) == 0) + ) + { + LeaveCriticalSection( &NwServiceListCriticalSection ); + + return pSvc; + } + } + + LeaveCriticalSection( &NwServiceListCriticalSection ); + return NULL; +} + +DWORD +SapFunc( + HANDLE hEventHandle + ) +/*++ + +Routine Description: + + This routine is a separate thread that wakes up every 60 seconds + and advertise all the service contained in the service link list + that are not advertised by the SAP service. + +Arguments: + + hEventHandle - used to notify thread that server is stopping + +Return Value: + + Win32 error. + +--*/ +{ + DWORD err = NO_ERROR; + + // + // This thread loops until the service is shut down or when some error + // occurred in WaitForSingleObject + // + + while ( TRUE ) + { + DWORD rc; + + if ( hEventHandle != NULL ) + { + rc = WaitForSingleObject( hEventHandle, SAP_ADVERTISE_FREQUENCY ); + } + else + { + Sleep( SAP_ADVERTISE_FREQUENCY ); + rc = WAIT_TIMEOUT; + } + + if ( rc == WAIT_FAILED ) + { + err = GetLastError(); + break; + } + else if ( rc == WAIT_OBJECT_0 ) + { + // + // The service is stopping, break out of the loop and + // return, thus terminating the thread + // + break; + } + else if ( rc == WAIT_TIMEOUT ) + { + PREGISTERED_SERVICE pSvc; + SOCKADDR_IPX bindAddr; + DWORD fGetAddr; + + fGetAddr = FALSE; + + // + // Time out occurred, time to send the SAP advertise packets + // + + EnterCriticalSection( &NwServiceListCriticalSection ); + + if ( pServiceListHead == NULL ) + { + LeaveCriticalSection( &NwServiceListCriticalSection ); + + // + // Clean up the SAP interface + // + (VOID) SapLibShutdown(); + + // + // Clean up the socket interface + // + if ( fInitSocket ) + { + closesocket( socketSap ); +// (VOID) WSACleanup(); + } + + break; + } + + for ( pSvc = pServiceListHead; pSvc != NULL; pSvc = pSvc->Next ) + { + if ( !pSvc->fAdvertiseBySap ) + { + // + // Ignore the error since we can't return + // nor pop up the error + // + + SOCKADDR_IPX *pAddr = (SOCKADDR_IPX *) + pSvc->pServiceInfo->lpServiceAddress->Addresses[0].lpAddress; + SOCKADDR_IPX *pAddrToAdvertise = pAddr; + SOCKADDR_IPX newAddr; + + if ( !memcmp( pAddr->sa_netnum, + "\x00\x00\x00\x00", + IPX_ADDRESS_NETNUM_LENGTH )) + { + + if ( !fGetAddr ) + { + DWORD len = sizeof( SOCKADDR_IPX ); + + rc = getsockname( socketSap, + (PSOCKADDR) &bindAddr, + &len ); + + if ( rc != SOCKET_ERROR ) + fGetAddr = TRUE; + } + + if ( fGetAddr ) + { + // copy the ipx address to advertise + memcpy( &newAddr, + pAddr, + sizeof( SOCKADDR_IPX)); + + // replace the net number with the correct one + memcpy( &(newAddr.sa_netnum), + &(bindAddr.sa_netnum), + IPX_ADDRESS_NETNUM_LENGTH ); + + pAddr = &newAddr; + } + } + + (VOID) NwAdvertiseService( + pSvc->pServiceInfo->lpServiceName, + pSvc->nSapType, + pAddr, + hEventHandle ); + } + } + + LeaveCriticalSection( &NwServiceListCriticalSection ); + } + } + + return err; +} |