399 lines
12 KiB
C
399 lines
12 KiB
C
/********************************************************************++
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
PARTICULAR PURPOSE.
|
|
|
|
Copyright (c) Microsoft Corporation. All Rights Reserved.
|
|
|
|
Module Name:
|
|
|
|
SampleProvider.c
|
|
|
|
Abstract:
|
|
|
|
This C file includes sample code for a simple nspv2 naming
|
|
provider.
|
|
|
|
--********************************************************************/
|
|
|
|
#include "SampleProvider.h"
|
|
|
|
// NOTE: *** Every provider should generate their own unique id ***
|
|
//
|
|
// if you use this sample as the basis for your own provider, you must change the next line.
|
|
GUID g_SampleProviderId =
|
|
{ 0xe6401bf9, 0x5579, 0x472a, { 0x83, 0x82, 0xca, 0x07, 0x07, 0x66, 0x11, 0xbd }};
|
|
|
|
|
|
typedef struct _LookupRequest
|
|
{
|
|
PWSTR pwzEmailAddress;
|
|
PWSTR pwzServiceName;
|
|
BOOL fPreferredName;
|
|
BOOL fProcessed;
|
|
} LookupRequest;
|
|
|
|
|
|
// Utility routine that calculates the size of a WSAQUERYSET2 based on a specific
|
|
// email address and array of CSADDR_INFO.
|
|
//
|
|
size_t CalculateSizeHelper(PCWSTR pcwzEmail,
|
|
ULONG cAddresses,
|
|
__in_ecount(cAddresses) CSADDR_INFO *prgAddresses)
|
|
{
|
|
WSAQUERYSET2 qs = {0};
|
|
|
|
// Fill out our temp queryset w/ the required fields.
|
|
//
|
|
qs.lpszServiceInstanceName = (PWSTR) pcwzEmail;
|
|
qs.dwNumberOfCsAddrs = cAddresses;
|
|
qs.lpcsaBuffer = prgAddresses;
|
|
qs.lpNSProviderId = &g_SampleProviderId;
|
|
|
|
// Call the generic utility from utils.c
|
|
//
|
|
return GetWSAQuerySet2Size(&qs);
|
|
}
|
|
|
|
//
|
|
// NSP Function Entry Points
|
|
//
|
|
|
|
// Called each time a new client process begins using the provider Providers may use the
|
|
// ppvClientSessionArg field to store information about this session -- the value returned
|
|
// from this function will be passed to subsequent calls in the same session.
|
|
//
|
|
INT WSAAPI NSPv2Startup(__in LPGUID lpguidProvider,
|
|
__out LPVOID* ppvClientSessionArg)
|
|
{
|
|
lpguidProvider; //unreferenced param
|
|
ppvClientSessionArg; //unreferenced param
|
|
|
|
*ppvClientSessionArg = NULL;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// Called when a client is shutting down. The ppvClientSessionArg is the same one returned
|
|
// from this provider from the call to NSPv2Startup.
|
|
//
|
|
INT WSAAPI NSPv2Cleanup(__in LPGUID lpguidProvider,
|
|
__in LPVOID* ppvClientSessionArg)
|
|
{
|
|
lpguidProvider; //unreferenced param
|
|
ppvClientSessionArg; //unreferenced param
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
// Called when a new lookup is beginning. The provider should store any required information
|
|
// in order to perform the actual lookup, which will happen in NSPv2LookupServiceNextEx.
|
|
// The provider returns a "handle" in phLookup, which will be passed back to the provider
|
|
// in NSPv2LookupServiceNextEx and NSPv2LookupServiceEnd to keep track of which lookup session
|
|
// is being used.
|
|
//
|
|
INT WSAAPI NSPv2LookupServiceBegin(__in LPGUID lpguidProvider,
|
|
__in LPWSAQUERYSET2 pqsRestrictions,
|
|
DWORD dwControlFlags,
|
|
__in LPVOID pvClientSessionArg,
|
|
__out LPHANDLE phLookup)
|
|
{
|
|
INT err = NO_ERROR;
|
|
LookupRequest *pRequest = malloc(sizeof(LookupRequest));
|
|
|
|
lpguidProvider; //unreferenced param
|
|
pvClientSessionArg; //unreferenced param
|
|
|
|
if (pRequest == NULL)
|
|
{
|
|
err = WSA_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
ZeroMemory(pRequest, sizeof(*pRequest));
|
|
pRequest->fProcessed = FALSE;
|
|
|
|
if (dwControlFlags & LUP_RETURN_PREFERRED_NAMES)
|
|
{
|
|
pRequest->fPreferredName = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pRequest->fPreferredName = FALSE;
|
|
err = CopyString(pqsRestrictions->lpszServiceInstanceName, &pRequest->pwzEmailAddress);
|
|
if (err != NO_ERROR)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
err = CopyString(pqsRestrictions->lpszContext, &pRequest->pwzServiceName);
|
|
if (err != NO_ERROR)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (err != NO_ERROR)
|
|
{
|
|
SetLastError(err);
|
|
return SOCKET_ERROR;
|
|
}
|
|
else
|
|
{
|
|
*phLookup = (HANDLE) pRequest;
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
VOID WSAAPI NSPv2LookupServiceNextEx( HANDLE hAsyncCall,
|
|
HANDLE hLookup,
|
|
DWORD dwControlFlags,
|
|
__inout LPDWORD pdwBufLen,
|
|
__out LPWSAQUERYSET2 pqsResults)
|
|
{
|
|
INT err = NO_ERROR;
|
|
ULONG cAddresses = 0;
|
|
CSADDR_INFO *pAddresses = NULL;
|
|
LookupRequest *pRequest = (LookupRequest*) hLookup;
|
|
PWSTR pwzEmailAddress = NULL;
|
|
|
|
dwControlFlags; //unreferenced param
|
|
|
|
if (pdwBufLen == NULL)
|
|
{
|
|
SetLastError(WSA_INVALID_PARAMETER);
|
|
WSAProviderCompleteAsyncCall(hAsyncCall, WSA_INVALID_PARAMETER);
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(pqsResults, *pdwBufLen);
|
|
|
|
if (pRequest->fProcessed == TRUE)
|
|
{
|
|
// we have already returned a result for this request, and we only support one.
|
|
err = WSA_E_NO_MORE;
|
|
}
|
|
else
|
|
{
|
|
if (pRequest->fPreferredName)
|
|
{
|
|
PWSTR wzPreferred = L"You@sampleprovider.net";
|
|
|
|
pAddresses = NULL;
|
|
cAddresses = 0;
|
|
pwzEmailAddress = wzPreferred;
|
|
}
|
|
else
|
|
{
|
|
RegInfo *pRegInfo = NULL;
|
|
|
|
// The pRegInfo returned from FindRegistration is just a pointer to an item in an internal
|
|
// array that could go away, based on another call to SetService. Thus, we enter a
|
|
// critical section to protect that.
|
|
EnterCriticalSection(&g_cs);
|
|
|
|
// Lookup the email address. In our implementation this is just a lookup in a local cache.
|
|
//
|
|
if (FindRegistration(pRequest->pwzEmailAddress, pRequest->pwzServiceName, &pRegInfo))
|
|
{
|
|
pAddresses = pRegInfo->prgAddresses;
|
|
cAddresses = pRegInfo->cAddresses;
|
|
pwzEmailAddress = pRegInfo->pwzEmailAddress;
|
|
}
|
|
else
|
|
{
|
|
// Failed to find an entry for this email address in our cache.
|
|
//
|
|
err = WSANO_DATA;
|
|
}
|
|
|
|
LeaveCriticalSection(&g_cs);
|
|
}
|
|
}
|
|
|
|
if (err == NO_ERROR)
|
|
{
|
|
// Found a registration! Figure out how much space is required to return the data
|
|
// to the caller.
|
|
//
|
|
size_t cbRequired = CalculateSizeHelper(pwzEmailAddress,
|
|
cAddresses,
|
|
pAddresses);
|
|
|
|
if (*pdwBufLen < cbRequired)
|
|
{
|
|
// They didn't pass us a large enough buffer. Tell them how much we need, and
|
|
// return WSAEFAULT.
|
|
//
|
|
*pdwBufLen = (DWORD) cbRequired;
|
|
err = WSAEFAULT;
|
|
}
|
|
else
|
|
{
|
|
// They passed us a large enough buffer, let's fill it out.
|
|
//
|
|
WSAQUERYSET2 qs = {0};
|
|
|
|
qs.dwSize = sizeof(WSAQUERYSET2);
|
|
qs.dwNameSpace = NS_EMAIL;
|
|
qs.lpszServiceInstanceName = pwzEmailAddress;
|
|
qs.dwNumberOfCsAddrs = cAddresses;
|
|
qs.lpcsaBuffer = pAddresses;
|
|
qs.lpNSProviderId = &g_SampleProviderId;
|
|
|
|
if (pRequest->fPreferredName)
|
|
qs.dwOutputFlags = LUP_RETURN_PREFERRED_NAMES;
|
|
|
|
// We need to hand back our memory in buffer from a single allocation (pointers in
|
|
// the WSAQUERYSET2 structure need to simply point to memory later in the same
|
|
// allocation). We call our utility routine to perform this serialization.
|
|
err = BuildSerializedQuerySet2(&qs, *pdwBufLen, (BYTE*) pqsResults);
|
|
|
|
if (err == NO_ERROR)
|
|
{
|
|
// Update the request handle, to remember we have already processed this one.
|
|
pRequest->fProcessed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// It is safe to complete the call on the same thread. In fact, even if this call had
|
|
// take a "long time" -- the calling threads would not have been blocked. The design is
|
|
// such that naming providers need not worry about this.
|
|
WSAProviderCompleteAsyncCall(hAsyncCall, err);
|
|
}
|
|
|
|
// Called when a particular lookup is complete. In our case, we need to free the memory
|
|
// associated w/ our "handle".
|
|
//
|
|
INT WSAAPI NSPv2LookupServiceEnd(HANDLE hLookup)
|
|
{
|
|
LookupRequest *pRequest = (LookupRequest *) hLookup;
|
|
|
|
if (pRequest != NULL)
|
|
{
|
|
free(pRequest->pwzEmailAddress);
|
|
free(pRequest->pwzServiceName);
|
|
free(pRequest);
|
|
return NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
SetLastError(WSA_INVALID_PARAMETER);
|
|
return SOCKET_ERROR;
|
|
}
|
|
}
|
|
|
|
// Called when the client wants to add or remove a new registration for the specified email address.
|
|
//
|
|
void WSAAPI NSPv2SetServiceEx( HANDLE hAsyncCall,
|
|
__in LPGUID pProviderGuid,
|
|
__in LPWSAQUERYSET2 pQuerySet,
|
|
WSAESETSERVICEOP eSetServiceOp,
|
|
DWORD dwControlFlags,
|
|
__in LPVOID pvClientSessionArg)
|
|
{
|
|
INT retval = NO_ERROR;
|
|
|
|
pProviderGuid; //unreferenced param
|
|
dwControlFlags; //unreferenced param
|
|
pvClientSessionArg; //unreferenced param
|
|
|
|
if (eSetServiceOp == RNRSERVICE_REGISTER)
|
|
{
|
|
retval = AddRegistration(pQuerySet);
|
|
}
|
|
else
|
|
{
|
|
retval = RemoveRegistration(pQuerySet);
|
|
}
|
|
|
|
WSAProviderCompleteAsyncCall(hAsyncCall, retval);
|
|
}
|
|
|
|
void __cdecl main(int argc, __in_ecount(argc) char *argv[])
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WSADATA wsaData = { 0 };
|
|
WORD wVersionRequested = MAKEWORD( 2, 2 );
|
|
|
|
INT iErr = WSAStartup(wVersionRequested, &wsaData);
|
|
if (iErr != 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(iErr);
|
|
return;
|
|
}
|
|
|
|
if (argc < 2)
|
|
{
|
|
printf("Usage: %s [-install] [-run]\n", argv[0]);
|
|
}
|
|
else if (_stricmp(argv[1], "/install") == 0 ||
|
|
_stricmp(argv[1], "-install") == 0)
|
|
{
|
|
INT iResult = DoInstall(g_SampleProviderId);
|
|
if (iResult != NO_ERROR)
|
|
{
|
|
// If the service provider is already installed, this will return
|
|
// an error. Reinstalling the service provider will insure there
|
|
// are no other installation issues.
|
|
//
|
|
DoUninstall(g_SampleProviderId);
|
|
iResult = DoInstall(g_SampleProviderId);
|
|
}
|
|
|
|
if (iResult != NO_ERROR)
|
|
{
|
|
printf("Failed to install provider. Be sure you are running as admin. Err=0x%x\n", WSAGetLastError());
|
|
}
|
|
else
|
|
{
|
|
printf("Successfully installed provider. Please start service by running: %s [-run]\n", argv[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We are doing normal startup. Need to inform winsock about the entry points
|
|
// to our NSP routines
|
|
//
|
|
NSPV2_ROUTINE NspRoutines = {0};
|
|
|
|
NspRoutines.cbSize = sizeof(NspRoutines);
|
|
NspRoutines.dwMajorVersion = 1;
|
|
NspRoutines.dwMinorVersion = 0;
|
|
NspRoutines.NSPv2Startup = NSPv2Startup;
|
|
NspRoutines.NSPv2Cleanup = NSPv2Cleanup;
|
|
NspRoutines.NSPv2LookupServiceBegin = NSPv2LookupServiceBegin;
|
|
NspRoutines.NSPv2LookupServiceNextEx = NSPv2LookupServiceNextEx;
|
|
NspRoutines.NSPv2LookupServiceEnd = NSPv2LookupServiceEnd;
|
|
NspRoutines.NSPv2SetServiceEx = NSPv2SetServiceEx;
|
|
NspRoutines.NSPv2ClientSessionRundown = NULL;
|
|
|
|
InitializeCriticalSection(&g_cs);
|
|
|
|
// Inform winsock that we are ready to receive registrations and lookups,
|
|
//
|
|
if (WSAAdvertiseProvider(&g_SampleProviderId, &NspRoutines) != 0)
|
|
{
|
|
printf("WSAAdvertiseProvider failed. (0x%x)\n", WSAGetLastError());
|
|
return;
|
|
}
|
|
|
|
printf("Hit Enter to quit...\n");
|
|
getchar();
|
|
|
|
// All done, do some cleanup
|
|
//
|
|
WSAUnadvertiseProvider(&g_SampleProviderId);
|
|
RemoveAllRegistrations();
|
|
}
|
|
|
|
if (WSACleanup() != NO_ERROR)
|
|
{
|
|
fprintf(stderr, "WSACleanup failed WSAError = 0x%x\n", WSAGetLastError());
|
|
}
|
|
}
|