mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
740 lines
16 KiB
740 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Epts.c
|
|
|
|
Abstract:
|
|
|
|
Common code to listen to endpoints in the DCOM service.
|
|
|
|
Author:
|
|
|
|
Mario Goertzel [MarioGo]
|
|
|
|
Revision History:
|
|
|
|
MarioGo 6/16/1995 Bits 'n pieces
|
|
|
|
--*/
|
|
|
|
#include <dcomss.h>
|
|
#include <winsvc.h>
|
|
#include <winsock.h>
|
|
#include <wsipx.h>
|
|
#include <nspapi.h>
|
|
|
|
// Globals
|
|
|
|
BOOL gfDelayedAdvertiseSaps = FALSE;
|
|
|
|
typedef enum
|
|
{
|
|
SapStateUnknown,
|
|
SapStateNoServices,
|
|
SapStateEnabled
|
|
} SAP_STATE;
|
|
|
|
SAP_STATE SapState = SapStateUnknown;
|
|
|
|
// Prototypes
|
|
|
|
void AdvertiseNameWithSap(BOOL fServiceCheck);
|
|
|
|
//
|
|
// The index is the protseq tower id.
|
|
// BUGBUG - this info should be read from the registry.
|
|
//
|
|
|
|
PROTSEQ_INFO
|
|
gaProtseqInfo[] =
|
|
{
|
|
/* 0x00 */ { STOPPED, 0, 0 },
|
|
/* 0x01 */ { STOPPED, 0, 0 },
|
|
/* 0x02 */ { STOPPED, 0, 0 },
|
|
/* 0x03 */ { STOPPED, 0, 0 },
|
|
/* 0x04 */ { STOPPED, L"ncacn_dnet_dsp", L"#69" },
|
|
/* 0x05 */ { STOPPED, 0, 0 },
|
|
/* 0x06 */ { STOPPED, 0, 0 },
|
|
/* 0x07 */ { STOPPED, L"ncacn_ip_tcp", L"135" },
|
|
/* 0x08 */ { STOPPED, L"ncadg_ip_udp", L"135" },
|
|
/* 0x09 */ { STOPPED, L"ncacn_nb_tcp", L"135" },
|
|
/* 0x0a */ { STOPPED, 0, 0 },
|
|
/* 0x0b */ { STOPPED, 0, 0 },
|
|
/* 0x0c */ { STOPPED, L"ncacn_spx", L"34280" },
|
|
/* 0x0d */ { STOPPED, L"ncacn_nb_ipx", L"135" },
|
|
/* 0x0e */ { STOPPED, L"ncadg_ipx", L"34280" },
|
|
/* 0x0f */ { STOPPED, L"ncacn_np", L"\\pipe\\epmapper" },
|
|
/* 0x10 */ { STOPPED, L"ncalrpc", L"epmapper" },
|
|
/* 0x11 */ { STOPPED, 0, 0 },
|
|
/* 0x12 */ { STOPPED, 0, 0 },
|
|
/* 0x13 */ { STOPPED, L"ncacn_nb_nb", L"135" },
|
|
/* 0x14 */ { STOPPED, 0, 0 },
|
|
/* 0x15 */ { STOPPED, 0, 0 }, // was ncacn_nb_xns - unsupported.
|
|
/* 0x16 */ { STOPPED, L"ncacn_at_dsp", L"Endpoint Mapper" },
|
|
/* 0x17 */ { STOPPED, L"ncadg_at_ddp", L"Endpoint Mapper" },
|
|
/* 0x18 */ { STOPPED, 0, 0 },
|
|
/* 0x19 */ { STOPPED, 0, 0 },
|
|
/* 0x1A */ { STOPPED, L"ncacn_vns_spp", L"385"}
|
|
};
|
|
|
|
#define PROTSEQ_IDS (sizeof(gaProtseqInfo)/sizeof(PROTSEQ_INFO))
|
|
|
|
#define ID_LPC (0x10)
|
|
#define ID_IPX (0x0E)
|
|
#define ID_SPX (0x0C)
|
|
|
|
|
|
RPC_STATUS
|
|
UseProtseqIfNecessary(
|
|
IN USHORT id
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Listens to the well known RPC endpoint mapper endpoint
|
|
for the protseq. Returns very quickly if the process
|
|
is already listening to the protseq.
|
|
|
|
Arguments:
|
|
|
|
id - the tower id of protseq. See GetProtseqId() if you don't
|
|
already have this value.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - no errors occured.
|
|
RPC_S_OUT_OF_RESOURCES - when we're unable to setup security for the endpoint.
|
|
RPC_S_INVALID_RPC_PROTSEQ - if id is unknown/invalid.
|
|
|
|
Any error from RpcServerUseProtseqEp.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS status = RPC_S_OK;
|
|
SECURITY_DESCRIPTOR sd, *psd;
|
|
RPC_POLICY Policy;
|
|
|
|
Policy.Length = sizeof(RPC_POLICY);
|
|
Policy.EndpointFlags = 0;
|
|
Policy.NICFlags = RPC_C_BIND_TO_ALL_NICS;
|
|
|
|
ASSERT(id);
|
|
|
|
if (id == 0 || id >= PROTSEQ_IDS)
|
|
{
|
|
ASSERT(0);
|
|
return(RPC_S_INVALID_RPC_PROTSEQ);
|
|
}
|
|
|
|
if (gaProtseqInfo[id].state == STARTED)
|
|
{
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
if (id == ID_LPC)
|
|
{
|
|
// ncalrpc needs a security descriptor.
|
|
|
|
psd = &sd;
|
|
|
|
InitializeSecurityDescriptor(
|
|
psd,
|
|
SECURITY_DESCRIPTOR_REVISION
|
|
);
|
|
|
|
if ( FALSE == SetSecurityDescriptorDacl (
|
|
psd,
|
|
TRUE, // Dacl present
|
|
NULL, // NULL Dacl
|
|
FALSE // Not defaulted
|
|
) )
|
|
{
|
|
status = RPC_S_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psd = 0;
|
|
}
|
|
|
|
if (status == RPC_S_OK )
|
|
{
|
|
status = RpcServerUseProtseqEpEx(gaProtseqInfo[id].pwstrProtseq,
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT + 1,
|
|
gaProtseqInfo[id].pwstrEndpoint,
|
|
psd,
|
|
&Policy);
|
|
|
|
// No locking is done here, the RPC runtime may return duplicate
|
|
// endpoint if two threads call this at the same time.
|
|
if (status == RPC_S_DUPLICATE_ENDPOINT)
|
|
{
|
|
ASSERT(gaProtseqInfo[id].state == STARTED);
|
|
status = RPC_S_OK;
|
|
}
|
|
|
|
#ifdef DEBUGRPC
|
|
if (status != RPC_S_OK)
|
|
{
|
|
DbgPrint("DCOMSS: Unable to listen to %S\n", gaProtseqInfo[id].pwstrProtseq);
|
|
}
|
|
#endif
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
gaProtseqInfo[id].state = STARTED;
|
|
|
|
if (id == ID_IPX || id == ID_SPX )
|
|
{
|
|
AdvertiseNameWithSap(TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
PWSTR
|
|
GetProtseq(
|
|
IN USHORT ProtseqId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the unicode protseq give the protseqs tower id.
|
|
|
|
Arguments:
|
|
|
|
ProtseqId - Tower id of the protseq in question.
|
|
|
|
Return Value:
|
|
|
|
NULL if the id is invalid.
|
|
|
|
non-NULL if the id is valid - note the pointer doesn't need to be freed.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(ProtseqId);
|
|
|
|
if (ProtseqId < PROTSEQ_IDS)
|
|
{
|
|
return(gaProtseqInfo[ProtseqId].pwstrProtseq);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
PWSTR
|
|
GetEndpoint(
|
|
IN USHORT ProtseqId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the well known endpoint associated with the protseq.
|
|
|
|
Arguments:
|
|
|
|
ProtseqId - the id (See GetProtseqId()) of the protseq in question.
|
|
|
|
Return Value:
|
|
|
|
0 - Unknown/invalid id.
|
|
|
|
!0 - The endpoint associated with the protseq.
|
|
note: should not be freed.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(ProtseqId);
|
|
|
|
if (ProtseqId < PROTSEQ_IDS)
|
|
{
|
|
return(gaProtseqInfo[ProtseqId].pwstrEndpoint);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
USHORT
|
|
GetProtseqId(
|
|
IN PWSTR Protseq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the tower id for a protseq.
|
|
|
|
This could be changed to a faster search, but remember that
|
|
eventually the table will NOT be static. (ie. we can't just
|
|
create a perfect hash based on the static table).
|
|
|
|
Arguments:
|
|
|
|
Protseq - a unicode protseq to lookup. It is assumed
|
|
to be non-null.
|
|
|
|
Return Value:
|
|
|
|
0 - unknown/invalid protseq
|
|
non-zero - the id.
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
ASSERT(Protseq);
|
|
|
|
for(i = 1; i < PROTSEQ_IDS; i++)
|
|
{
|
|
if ( 0 != gaProtseqInfo[i].pwstrProtseq
|
|
&& 0 == lstrcmpW(gaProtseqInfo[i].pwstrProtseq, Protseq))
|
|
{
|
|
return(i);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
USHORT
|
|
GetProtseqIdAnsi(
|
|
IN PSTR pstrProtseq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the tower id for a protseq.
|
|
|
|
This could be changed to a faster search, but remember that
|
|
eventually the table will NOT be static. (ie. we can't just
|
|
create a perfect hash based on the static table).
|
|
|
|
Arguments:
|
|
|
|
Protseq - an ansi (8 bit char) protseq to lookup. It is assumed
|
|
to be non-null.
|
|
|
|
Return Value:
|
|
|
|
0 - unknown/invalid protseq
|
|
non-zero - the id.
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
ASSERT(pstrProtseq);
|
|
|
|
for(i = 1; i < PROTSEQ_IDS; i++)
|
|
{
|
|
if (0 != gaProtseqInfo[i].pwstrProtseq)
|
|
{
|
|
PWSTR pwstrProtseq = gaProtseqInfo[i].pwstrProtseq;
|
|
PSTR pstrT = pstrProtseq;
|
|
|
|
while(*pstrT && *pwstrProtseq && *pstrT == *pwstrProtseq)
|
|
{
|
|
pstrT++;
|
|
pwstrProtseq++;
|
|
}
|
|
if (*pstrT == *pwstrProtseq)
|
|
{
|
|
return(i);
|
|
}
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
InitializeEndpointManager(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the dcom service starts.
|
|
|
|
BUGBUG: Should read the protseqs, tower IDs and endpoints from the registry.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OUT_OF_MEMORY - if needed
|
|
|
|
RPC_S_OUT_OF_RESOURCES - usually on registry failures.
|
|
|
|
--*/
|
|
{
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsLocal(
|
|
IN USHORT ProtseqId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if the protseq id is local-only. (ncalrpc)
|
|
|
|
Arguments:
|
|
|
|
ProtseqId - The id of the protseq in question.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if the protseq id is local-only
|
|
FALSE - if the protseq id invalid or available remotely.
|
|
|
|
--*/
|
|
{
|
|
return(ProtseqId == ID_LPC);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DelayedUseProtseq(
|
|
IN USHORT id
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If the protseq is not being used its state is changed
|
|
so that a callto CompleteDelayedUseProtseqs() will actually
|
|
cause the server to listen to the protseq.
|
|
|
|
This is called when an RPC server registers an dynamic
|
|
endpoint on this protocol.
|
|
|
|
Arguments:
|
|
|
|
id - the id of the protseq you wish to listen to.
|
|
|
|
Return Value:
|
|
|
|
0 - normally
|
|
|
|
RPC_S_INVALID_RPC_PROTSEQ - if id is invalid.
|
|
|
|
--*/
|
|
{
|
|
// For IPX and SPX
|
|
if (id == ID_IPX || id == ID_SPX)
|
|
{
|
|
gfDelayedAdvertiseSaps = TRUE;
|
|
}
|
|
|
|
if (id < PROTSEQ_IDS)
|
|
{
|
|
if (gaProtseqInfo[id].pwstrProtseq != 0)
|
|
{
|
|
if (gaProtseqInfo[id].state == STOPPED)
|
|
gaProtseqInfo[id].state = START;
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
}
|
|
return(RPC_S_INVALID_RPC_PROTSEQ);
|
|
}
|
|
|
|
|
|
VOID
|
|
CompleteDelayedUseProtseqs(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Start listening to any protseqs previously passed
|
|
to DelayedUseProtseq(). No errors are returned,
|
|
but informationals are printed on debug builds.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
USHORT i;
|
|
|
|
for(i = 1; i < PROTSEQ_IDS; i++)
|
|
{
|
|
if (START == gaProtseqInfo[i].state)
|
|
{
|
|
RPC_STATUS status = UseProtseqIfNecessary(i);
|
|
#ifdef DEBUGRPC
|
|
if (RPC_S_OK == status)
|
|
ASSERT(gaProtseqInfo[i].state == STARTED);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (gfDelayedAdvertiseSaps)
|
|
{
|
|
gfDelayedAdvertiseSaps = FALSE;
|
|
AdvertiseNameWithSap(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
ServiceInstalled(
|
|
PWSTR ServiceName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Tests if a service is installed.
|
|
|
|
Arguments:
|
|
|
|
ServiceName - The unicode name (short or long) of the service
|
|
to check.
|
|
|
|
Return Value:
|
|
|
|
0 - service installed
|
|
ERROR_SERVICE_DOES_NOT_EXIST - service not installed
|
|
other - parameter or resource problem
|
|
|
|
--*/
|
|
{
|
|
SC_HANDLE ScHandle;
|
|
SC_HANDLE ServiceHandle;
|
|
|
|
ScHandle = OpenSCManagerW(0, 0, GENERIC_READ);
|
|
|
|
if (ScHandle == 0)
|
|
{
|
|
return(GetLastError());
|
|
}
|
|
|
|
ServiceHandle = OpenService(ScHandle, ServiceName, GENERIC_READ);
|
|
|
|
if (ServiceHandle == 0)
|
|
{
|
|
#if DBG
|
|
if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
|
|
{
|
|
DbgPrint("OR: Failed %d opening the %S service\n",
|
|
GetLastError(), ServiceName);
|
|
}
|
|
#endif
|
|
|
|
CloseServiceHandle(ScHandle);
|
|
return(GetLastError());
|
|
}
|
|
|
|
// Service installed
|
|
|
|
CloseServiceHandle(ScHandle);
|
|
CloseServiceHandle(ServiceHandle);
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
|
|
const GUID RPC_SAP_SERVICE_TYPE = { 0x000b0640, 0, 0, { 0xC0,0,0,0,0,0,0,0x46 } };
|
|
|
|
void
|
|
AdvertiseNameWithSap(
|
|
BOOL fServiceCheck
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Is this server is listening to IPX/SPX then, depending
|
|
on what services are enabled on this machine, this
|
|
function will enable SAPs on this machines address. This
|
|
allows RPC clients to resolve the pretty name of this
|
|
server to a raw ipx address.
|
|
|
|
|
|
Arguments:
|
|
|
|
fServiceCheck - If true, this function will only advertise
|
|
with SAP if various services are installed. If false,
|
|
this will always turn on SAP.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
DWORD ignore;
|
|
|
|
// Service paramaters
|
|
NT_PRODUCT_TYPE type;
|
|
|
|
// GetComputerName parameters
|
|
WCHAR buffer[MAX_COMPUTERNAME_LENGTH + 1];
|
|
|
|
// winsock (socket, bind, getsockname) parameters
|
|
SOCKADDR_IPX ipxaddr;
|
|
SOCKET s;
|
|
int err;
|
|
int size;
|
|
|
|
// SetService params
|
|
SERVICE_INFOW info;
|
|
SERVICE_ADDRESSES addresses;
|
|
|
|
if ( SapState == SapStateEnabled
|
|
|| (fServiceCheck && (SapState == SapStateNoServices)) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (fServiceCheck)
|
|
{
|
|
// On servers, advertise if the NwSapAgent or NWCWorkstation
|
|
// services are installed. On workstations, advertise if
|
|
// NwSapAgent service is installed.
|
|
|
|
type = NtProductWinNt;
|
|
RtlGetNtProductType(&type);
|
|
|
|
status = ERROR_SERVICE_DOES_NOT_EXIST;
|
|
|
|
if (type != NtProductWinNt)
|
|
{
|
|
// Server platform, try NWCWorkstation
|
|
status = ServiceInstalled(L"NWCWorkstation");
|
|
}
|
|
|
|
if (status == ERROR_SERVICE_DOES_NOT_EXIST)
|
|
{
|
|
status = ServiceInstalled(L"NwSapAgent");
|
|
}
|
|
|
|
if (status == ERROR_SERVICE_DOES_NOT_EXIST)
|
|
{
|
|
SapState = SapStateNoServices;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Get this server's name
|
|
ignore = MAX_COMPUTERNAME_LENGTH + 1;
|
|
if (!GetComputerNameW(buffer, &ignore))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Get this server's IPX address..blech..
|
|
s = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );
|
|
if (s != -1)
|
|
{
|
|
size = sizeof(ipxaddr);
|
|
|
|
memset(&ipxaddr, 0, sizeof(ipxaddr));
|
|
ipxaddr.sa_family = AF_IPX;
|
|
|
|
err = bind(s, (struct sockaddr *)&ipxaddr, sizeof(ipxaddr));
|
|
if (err == 0)
|
|
{
|
|
err = getsockname(s, (struct sockaddr *)&ipxaddr, &size);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
err = -1;
|
|
}
|
|
|
|
if (err != 0)
|
|
{
|
|
#if DBG
|
|
DbgPrint("OR: socket/gesockname failed %d, aborting SAP setup\n",
|
|
GetLastError());
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (s != -1)
|
|
{
|
|
closesocket(s);
|
|
}
|
|
|
|
// We'll register only for the endpoint mapper port. The port
|
|
// value is not required but should be the same to avoid
|
|
// confusing routers keeping track of SAPs...
|
|
|
|
ipxaddr.sa_socket = 34280;
|
|
|
|
// Fill in the service info structure.
|
|
info.lpServiceType = (GUID *)&RPC_SAP_SERVICE_TYPE;
|
|
info.lpServiceName = buffer;
|
|
info.lpComment = L"RPC Services";
|
|
info.lpLocale = L"";
|
|
info.dwDisplayHint = 0;
|
|
info.dwVersion = 0;
|
|
info.dwTime = 0;
|
|
info.lpMachineName = buffer;
|
|
info.lpServiceAddress = &addresses;
|
|
info.ServiceSpecificInfo.cbSize = 0;
|
|
|
|
// Fill in the service addresses structure.
|
|
addresses.dwAddressCount = 1;
|
|
addresses.Addresses[0].dwAddressType = AF_IPX;
|
|
addresses.Addresses[0].dwAddressLength = sizeof(SOCKADDR_IPX);
|
|
addresses.Addresses[0].dwPrincipalLength = 0;
|
|
addresses.Addresses[0].lpAddress = (PBYTE)&ipxaddr;
|
|
addresses.Addresses[0].lpPrincipal = NULL;
|
|
|
|
// Set the service.
|
|
status = SetServiceW(NS_SAP,
|
|
SERVICE_REGISTER,
|
|
0,
|
|
&info,
|
|
NULL,
|
|
&ignore);
|
|
|
|
ASSERT(status == SOCKET_ERROR || status == 0);
|
|
if (status == SOCKET_ERROR)
|
|
{
|
|
status = GetLastError();
|
|
}
|
|
|
|
if (status == 0)
|
|
{
|
|
SapState = SapStateEnabled;
|
|
}
|
|
else
|
|
{
|
|
#if DBG
|
|
DbgPrint("OR: SetServiceW failed %d\n", status);
|
|
#endif
|
|
}
|
|
|
|
return;
|
|
}
|
|
|