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.
1375 lines
33 KiB
1375 lines
33 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
|
|
Edwardr 7/17/1996 Added ncadg_mq
|
|
Edwardr 5/01/1997 Added ncacn_http
|
|
MazharM 10-12.98 Add pnp stuff
|
|
KamenM Oct 2000 Removed ncadg_mq
|
|
|
|
--*/
|
|
|
|
//#define NCADG_MQ_ON
|
|
//#define NETBIOS_ON
|
|
#if !defined(_M_IA64)
|
|
#define SPX_ON
|
|
#endif
|
|
//#define IPX_ON
|
|
|
|
#if !defined(SPX_ON) && !defined(IPX_ON)
|
|
#define SPX_IPX_OFF
|
|
#endif
|
|
|
|
#include <dcomss.h>
|
|
#include <winsvc.h>
|
|
#include <winsock2.h>
|
|
|
|
#if !defined(SPX_IPX_OFF)
|
|
#include <wsipx.h>
|
|
#include <svcguid.h>
|
|
#include "sap.h"
|
|
#endif
|
|
|
|
// Globals
|
|
|
|
#if !defined(SPX_IPX_OFF)
|
|
const IPX_BOGUS_NETWORK_NUMBER = 0xefcd3412;
|
|
|
|
BOOL gfDelayedAdvertiseSaps = FALSE;
|
|
|
|
typedef enum
|
|
{
|
|
SapStateUnknown,
|
|
SapStateNoServices,
|
|
SapStateEnabled,
|
|
SapStateDisabled
|
|
} SAP_STATE;
|
|
|
|
SAP_STATE SapState = SapStateUnknown;
|
|
#endif
|
|
|
|
enum RegistryState
|
|
{
|
|
RegStateUnknown,
|
|
RegStateMissing,
|
|
RegStateYes,
|
|
RegStateNo
|
|
|
|
} RegistryState = RegStateUnknown;
|
|
|
|
// Prototypes
|
|
|
|
#if !defined(SPX_IPX_OFF)
|
|
void AdvertiseNameWithSap(void);
|
|
void CallSetService( SOCKADDR_IPX * pipxaddr, BOOL fRegister );
|
|
#endif
|
|
|
|
//
|
|
// The index is the protseq tower id.
|
|
//
|
|
|
|
PROTSEQ_INFO
|
|
gaProtseqInfo[] =
|
|
{
|
|
/* 0x00 */ { STOPPED, 0, 0 },
|
|
/* 0x01 */ { STOPPED, 0, 0 },
|
|
/* 0x02 */ { STOPPED, 0, 0 },
|
|
/* 0x03 */ { STOPPED, 0, 0 },
|
|
/* 0x04 */ { STOPPED, L"ncacn_dnet_nsp", 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" },
|
|
|
|
#ifdef NETBIOS_ON
|
|
/* 0x09 */ { STOPPED, L"ncacn_nb_tcp", L"135" },
|
|
#else
|
|
/* 0x09 */ { STOPPED, 0, 0 },
|
|
#endif
|
|
|
|
/* 0x0a */ { STOPPED, 0, 0 },
|
|
/* 0x0b */ { STOPPED, 0, 0 },
|
|
#if defined(SPX_ON)
|
|
/* 0x0c */ { STOPPED, L"ncacn_spx", L"34280" },
|
|
#else
|
|
/* 0x0c */ { STOPPED, 0, 0 },
|
|
#endif
|
|
|
|
#ifdef NETBIOS_ON
|
|
/* 0x0d */ { STOPPED, L"ncacn_nb_ipx", L"135" },
|
|
#else
|
|
/* 0x0d */ { STOPPED, 0, 0 },
|
|
#endif
|
|
|
|
/* 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 },
|
|
#ifdef NETBIOS_ON
|
|
/* 0x13 */ { STOPPED, L"ncacn_nb_nb", L"135" },
|
|
#else
|
|
/* 0x13 */ { STOPPED, 0, 0 },
|
|
#endif
|
|
|
|
/* 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, 0, 0 },
|
|
/* 0x1B */ { STOPPED, 0, 0 },
|
|
/* 0x1C */ { STOPPED, 0, 0 },
|
|
|
|
#ifdef NCADG_MQ_ON
|
|
/* 0x1D */ { STOPPED, L"ncadg_mq", L"EpMapper"},
|
|
#else
|
|
/* 0x1D */ { STOPPED, 0, 0 },
|
|
#endif
|
|
|
|
/* 0x1E */ { STOPPED, 0, 0 },
|
|
/* 0x1F */ { STOPPED, L"ncacn_http", L"593" }, // dcomhttp port assigned by IANA
|
|
/* 0x20 */ { STOPPED, 0, 0 },
|
|
};
|
|
|
|
#define PROTSEQ_IDS (sizeof(gaProtseqInfo)/sizeof(PROTSEQ_INFO))
|
|
|
|
#define ID_LPC (0x10)
|
|
#define ID_IPX (0x0E)
|
|
|
|
#if defined(SPX_ON)
|
|
#define ID_SPX (0x0C)
|
|
#endif
|
|
|
|
#define ID_HTTP (0x1F)
|
|
|
|
#define ID_TCP (0x07)
|
|
|
|
// Do not listen on all NICs by default.
|
|
// A reg key can be used to override this and listen on all NICs, but
|
|
// the override will break DG dynamic endpoint functionality when selective
|
|
// binding is enabled.
|
|
BOOL fListenOnInternet = FALSE; // see bug 69332 (in old nt raid db)
|
|
|
|
|
|
BOOL
|
|
CreateSids(
|
|
PSID* ppsidBuiltInAdministrators,
|
|
PSID* ppsidSystem,
|
|
PSID* ppsidWorld
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates and return pointers to three SIDs one for each of World,
|
|
Local Administrators, and System.
|
|
|
|
Arguments:
|
|
|
|
ppsidBuiltInAdministrators - Receives pointer to SID representing local
|
|
administrators;
|
|
ppsidSystem - Receives pointer to SID representing System;
|
|
ppsidWorld - Receives pointer to SID representing World.
|
|
|
|
Return Value:
|
|
|
|
BOOL indicating success (TRUE) or failure (FALSE).
|
|
|
|
Caller must free returned SIDs by calling FreeSid() for each returned
|
|
SID when this function return TRUE; pointers should be assumed garbage
|
|
when the function returns FALSE.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// An SID is built from an Identifier Authority and a set of Relative IDs
|
|
// (RIDs). The Authority of interest to us SECURITY_NT_AUTHORITY.
|
|
//
|
|
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
|
|
|
//
|
|
// Each RID represents a sub-unit of the authority. Local
|
|
// Administrators is in the "built in" domain. The other SIDs, for
|
|
// Authenticated users and system, is based directly off of the
|
|
// authority.
|
|
//
|
|
// For examples of other useful SIDs consult the list in
|
|
// \nt\public\sdk\inc\ntseapi.h.
|
|
//
|
|
|
|
if (!AllocateAndInitializeSid(&NtAuthority,
|
|
2, // 2 sub-authorities
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0,0,0,0,0,0,
|
|
ppsidBuiltInAdministrators)) {
|
|
|
|
// error
|
|
|
|
} else if (!AllocateAndInitializeSid(&NtAuthority,
|
|
1, // 1 sub-authorities
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0,0,0,0,0,0,0,
|
|
ppsidSystem)) {
|
|
|
|
// error
|
|
|
|
FreeSid(*ppsidBuiltInAdministrators);
|
|
*ppsidBuiltInAdministrators = NULL;
|
|
|
|
} else if (!AllocateAndInitializeSid(&WorldAuthority,
|
|
1, // 1 sub-authority
|
|
SECURITY_WORLD_RID,
|
|
0,0,0,0,0,0,0,
|
|
ppsidWorld)) {
|
|
|
|
// error
|
|
|
|
FreeSid(*ppsidBuiltInAdministrators);
|
|
*ppsidBuiltInAdministrators = NULL;
|
|
|
|
FreeSid(*ppsidSystem);
|
|
*ppsidSystem = NULL;
|
|
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
CreateSd(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates and return a SECURITY_DESCRIPTOR with a DACL granting
|
|
(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | SYNCHRONIZE) to World,
|
|
and GENERIC_ALL to Local Administrators and System.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Pointer to the created SECURITY_DESCRIPTOR, or NULL if an error occurred.
|
|
|
|
Caller must free returned SECURITY_DESCRIPTOR back to process heap by
|
|
a call to HeapFree.
|
|
|
|
--*/
|
|
{
|
|
PSID psidWorld;
|
|
PSID psidBuiltInAdministrators;
|
|
PSID psidSystem;
|
|
|
|
if (!CreateSids(&psidBuiltInAdministrators,
|
|
&psidSystem,
|
|
&psidWorld)) {
|
|
|
|
// error
|
|
|
|
} else {
|
|
|
|
//
|
|
// Calculate the size of and allocate a buffer for the DACL, we need
|
|
// this value independently of the total alloc size for ACL init.
|
|
//
|
|
|
|
PSECURITY_DESCRIPTOR Sd = NULL;
|
|
ULONG AclSize;
|
|
|
|
//
|
|
// "- sizeof (ULONG)" represents the SidStart field of the
|
|
// ACCESS_ALLOWED_ACE. Since we're adding the entire length of the
|
|
// SID, this field is counted twice.
|
|
//
|
|
|
|
AclSize = sizeof (ACL) +
|
|
(3 * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (ULONG))) +
|
|
GetLengthSid(psidWorld) +
|
|
GetLengthSid(psidBuiltInAdministrators) +
|
|
GetLengthSid(psidSystem);
|
|
|
|
Sd = HeapAlloc(GetProcessHeap(),
|
|
0,
|
|
SECURITY_DESCRIPTOR_MIN_LENGTH + AclSize);
|
|
|
|
if (!Sd) {
|
|
|
|
// error
|
|
|
|
} else {
|
|
|
|
ACL *Acl;
|
|
|
|
Acl = (ACL *)((BYTE *)Sd + SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
|
|
if (!InitializeAcl(Acl,
|
|
AclSize,
|
|
ACL_REVISION)) {
|
|
|
|
// error
|
|
|
|
} else if (!AddAccessAllowedAce(Acl,
|
|
ACL_REVISION,
|
|
SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE,
|
|
psidWorld)) {
|
|
|
|
// Failed to build the ACE granting "WORLD"
|
|
// (SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE) access.
|
|
|
|
} else if (!AddAccessAllowedAce(Acl,
|
|
ACL_REVISION,
|
|
GENERIC_ALL,
|
|
psidBuiltInAdministrators)) {
|
|
|
|
// Failed to build the ACE granting "Built-in Administrators"
|
|
// (GENERIC_ALL) access.
|
|
|
|
} else if (!AddAccessAllowedAce(Acl,
|
|
ACL_REVISION,
|
|
GENERIC_ALL,
|
|
psidSystem)) {
|
|
|
|
// Failed to build the ACE granting "System"
|
|
// GENERIC_ALL access.
|
|
|
|
} else if (!InitializeSecurityDescriptor(Sd,
|
|
SECURITY_DESCRIPTOR_REVISION)) {
|
|
|
|
// error
|
|
|
|
} else if (!SetSecurityDescriptorDacl(Sd,
|
|
TRUE,
|
|
Acl,
|
|
FALSE)) {
|
|
|
|
// error
|
|
|
|
} else {
|
|
FreeSid(psidWorld);
|
|
FreeSid(psidBuiltInAdministrators);
|
|
FreeSid(psidSystem);
|
|
|
|
return Sd;
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(),
|
|
0,
|
|
Sd);
|
|
}
|
|
|
|
FreeSid(psidWorld);
|
|
FreeSid(psidBuiltInAdministrators);
|
|
FreeSid(psidSystem);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
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 *psd = NULL;
|
|
RPC_POLICY Policy;
|
|
|
|
Policy.Length = sizeof(RPC_POLICY);
|
|
Policy.EndpointFlags = 0;
|
|
|
|
if (fListenOnInternet)
|
|
{
|
|
Policy.NICFlags = RPC_C_BIND_TO_ALL_NICS;
|
|
}
|
|
else
|
|
{
|
|
Policy.NICFlags = 0;
|
|
}
|
|
|
|
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 = CreateSd();
|
|
|
|
if ( NULL == psd )
|
|
{
|
|
status = RPC_S_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psd = NULL;
|
|
}
|
|
|
|
if (status == RPC_S_OK )
|
|
{
|
|
status = RpcServerUseProtseqEpEx(gaProtseqInfo[id].pwstrProtseq,
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT + 40,
|
|
gaProtseqInfo[id].pwstrEndpoint,
|
|
psd,
|
|
&Policy);
|
|
|
|
if ( NULL != psd )
|
|
{
|
|
HeapFree(GetProcessHeap(),
|
|
0,
|
|
psd);
|
|
|
|
psd = NULL;
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
status = RPC_S_OK;
|
|
}
|
|
|
|
#ifdef DEBUGRPC
|
|
if (status != RPC_S_OK)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"DCOMSS: Unable to listen to %S (0x%x)\n",
|
|
gaProtseqInfo[id].pwstrProtseq,
|
|
status));
|
|
}
|
|
#endif
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
gaProtseqInfo[id].state = STARTED;
|
|
|
|
#if !defined(SPX_IPX_OFF)
|
|
if (
|
|
#if defined(IPX_ON)
|
|
(id == ID_IPX)
|
|
||
|
|
#endif
|
|
#if defined(SPX_ON)
|
|
(id == ID_SPX)
|
|
#endif
|
|
)
|
|
{
|
|
UpdateSap(SAP_CTRL_MAYBE_REGISTER);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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((USHORT)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((USHORT)i);
|
|
}
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
const PWSTR NICConfigKey = L"System\\CurrentControlSet\\Services\\RpcSs";
|
|
const PWSTR ListenOnInternet = L"ListenOnInternet";
|
|
|
|
|
|
RPC_STATUS
|
|
InitializeEndpointManager(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the dcom service starts.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OUT_OF_MEMORY - if needed
|
|
|
|
RPC_S_OUT_OF_RESOURCES - usually on registry failures.
|
|
|
|
--*/
|
|
{
|
|
HKEY hkey;
|
|
DWORD size, type, value;
|
|
RPC_STATUS status;
|
|
|
|
status = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
(PWSTR)NICConfigKey,
|
|
0,
|
|
KEY_READ,
|
|
&hkey);
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
ASSERT(status == ERROR_FILE_NOT_FOUND);
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
size = sizeof(value);
|
|
status = RegQueryValueExW(hkey,
|
|
(PWSTR)ListenOnInternet,
|
|
0,
|
|
&type,
|
|
(PBYTE)&value,
|
|
&size);
|
|
|
|
if ( status == RPC_S_OK )
|
|
{
|
|
if ((type != REG_SZ)
|
|
|| (*(PWSTR)&value != 'Y'
|
|
&& *(PWSTR)&value != 'y'
|
|
&& *(PWSTR)&value != 'N'
|
|
&& *(PWSTR)&value != 'n'))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (*(PWSTR)&value == 'Y'
|
|
|| *(PWSTR)&value == 'y')
|
|
{
|
|
fListenOnInternet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fListenOnInternet = FALSE;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
RegCloseKey(hkey);
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
#if !defined(SPX_IPX_OFF)
|
|
// For IPX and SPX
|
|
if (
|
|
#if defined(IPX_ON)
|
|
(id == ID_IPX)
|
|
||
|
|
#endif
|
|
#if defined(SPX_ON)
|
|
(id == ID_SPX)
|
|
#endif
|
|
)
|
|
{
|
|
gfDelayedAdvertiseSaps = TRUE;
|
|
}
|
|
#endif
|
|
|
|
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 !defined(SPX_IPX_OFF)
|
|
if (gfDelayedAdvertiseSaps)
|
|
{
|
|
gfDelayedAdvertiseSaps = FALSE;
|
|
UpdateSap(SAP_CTRL_MAYBE_REGISTER);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !defined(SPX_IPX_OFF)
|
|
|
|
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(ERROR_SERVICE_DOES_NOT_EXIST);
|
|
}
|
|
|
|
ServiceHandle = OpenService(ScHandle, ServiceName, GENERIC_READ);
|
|
|
|
if (ServiceHandle == 0)
|
|
{
|
|
#if DBG
|
|
if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Failed %d opening the %S service\n",
|
|
GetLastError(),
|
|
ServiceName));
|
|
}
|
|
#endif
|
|
|
|
CloseServiceHandle(ScHandle);
|
|
return(ERROR_SERVICE_DOES_NOT_EXIST);
|
|
}
|
|
|
|
// Service installed
|
|
|
|
CloseServiceHandle(ScHandle);
|
|
CloseServiceHandle(ServiceHandle);
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
|
|
const GUID RPC_SAP_SERVICE_TYPE = SVCID_NETWARE(0x640);
|
|
|
|
void
|
|
UpdateSap(
|
|
enum SAP_CONTROL_TYPE action
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starts, stops, or updates the periodic SPX SAP broadcasts that allow an RPC
|
|
client to map the server name to an IPX address. To understand IPX and SAP,
|
|
read "IPX Router Specification", Novell part # 107-000029-001.
|
|
|
|
A SAP broadcast will be processed by several categories of machines
|
|
- all machines in the local subnet(s) will have to read and discard the packet
|
|
- routers connected to the local subnet(s) will add the data to the info they
|
|
periodically exchange with other routers
|
|
- Netware-compatible servers will add the info to their Bindery tables.
|
|
|
|
That is why not all NT machines should SAP.
|
|
|
|
Arguments:
|
|
|
|
action:
|
|
|
|
SAP_CTRL_FORCE_REGISTER: begin sapping
|
|
|
|
SAP_CTRL_MAYBE_REGISTER: begin sapping only if the Netware-compatible
|
|
workstation and/or the SAP Agent service is
|
|
installed. File/Print Svcs for Netware forces the
|
|
SAP Agent, so it too will enable sapping.
|
|
|
|
SAP_CTRL_UPDATE_ADDRESS: a net card was added or subtracted, or the network
|
|
address changed. Re-register if sapping is already
|
|
active.
|
|
|
|
SAP_CTRL_UNREGISTER: stop sapping
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
HKEY hKey;
|
|
|
|
// Service paramaters
|
|
NT_PRODUCT_TYPE type;
|
|
|
|
if (RegistryState == RegStateUnknown)
|
|
{
|
|
// The registry key has absolute control of SAPing
|
|
|
|
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
TEXT("Software\\Microsoft\\Rpc"),
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
|
|
if (status == ERROR_SUCCESS)
|
|
{
|
|
WCHAR pwstrValue[8];
|
|
DWORD dwType, dwLenBuffer;
|
|
dwLenBuffer = sizeof(pwstrValue);
|
|
|
|
|
|
status = RegQueryValueEx(hKey,
|
|
TEXT("AdvertiseRpcService"),
|
|
0,
|
|
&dwType,
|
|
(PBYTE)pwstrValue,
|
|
&dwLenBuffer
|
|
);
|
|
|
|
if ( status == ERROR_SUCCESS
|
|
&& dwType == REG_SZ)
|
|
{
|
|
if ( pwstrValue[0] == 'y'
|
|
|| pwstrValue[0] == 'Y' )
|
|
{
|
|
RegistryState = RegStateYes;
|
|
}
|
|
else if ( pwstrValue[0] == 'n'
|
|
|| pwstrValue[0] == 'N' )
|
|
{
|
|
RegistryState = RegStateNo;
|
|
}
|
|
else
|
|
{
|
|
// Value in the registry is wrong, pretend it doesn't exist.
|
|
RegistryState = RegStateMissing;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Bad or missing value in the registry, pretend is doesn't exist.
|
|
RegistryState = RegStateMissing;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
switch (action)
|
|
{
|
|
case SAP_CTRL_FORCE_REGISTER:
|
|
if (RegistryState == RegStateNo)
|
|
{
|
|
// "no" in registry trumps any registration
|
|
return;
|
|
}
|
|
|
|
if (SapState == SapStateEnabled)
|
|
{
|
|
// already active
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case SAP_CTRL_MAYBE_REGISTER:
|
|
if (RegistryState == RegStateNo)
|
|
{
|
|
// "no" in registry trumps any registration
|
|
return;
|
|
}
|
|
|
|
if (SapState == SapStateEnabled)
|
|
{
|
|
// already registered
|
|
return;
|
|
}
|
|
|
|
if (RegistryState == RegStateYes)
|
|
{
|
|
// don't check services, just register.
|
|
break;
|
|
}
|
|
|
|
if (SapState == SapStateNoServices)
|
|
{
|
|
ASSERT( RegistryState != RegStateYes ); // in case checks are rearranged
|
|
// the appropriate services are not installed
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Getting here means we don't know yet whether the proper services are installed.
|
|
//
|
|
// Depending on configuration, this controls if automatic
|
|
// listens (due to DCOM configuration) enable SAPing or not.
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Proper services are installed.
|
|
//
|
|
break;
|
|
|
|
case SAP_CTRL_UPDATE_ADDRESS:
|
|
if (SapState != SapStateEnabled)
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case SAP_CTRL_UNREGISTER:
|
|
if (SapState == SapStateDisabled ||
|
|
SapState == SapStateNoServices)
|
|
{
|
|
// already not registered
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT( 0 );
|
|
}
|
|
|
|
AdvertiseNameWithSap();
|
|
}
|
|
|
|
|
|
void
|
|
AdvertiseNameWithSap()
|
|
/*++
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
// winsock (socket, bind, getsockname) parameters
|
|
SOCKADDR_IPX new_ipxaddr;
|
|
static SOCKADDR_IPX old_ipxaddr = { AF_IPX, { 0 }, { 0 }, 0 } ;
|
|
static CRITICAL_SECTION * pCritsec;
|
|
|
|
SOCKET s;
|
|
int err;
|
|
int size;
|
|
|
|
//
|
|
// A critical section protects old_ipxaddr since several different events lead to
|
|
// calling this function. The following code makes sure that the critical
|
|
// section is created, and that all threads are using the same one.
|
|
//
|
|
if (!pCritsec)
|
|
{
|
|
CRITICAL_SECTION * myCritsec = HeapAlloc( GetProcessHeap(), 0, sizeof(CRITICAL_SECTION));
|
|
if (!myCritsec)
|
|
{
|
|
return;
|
|
}
|
|
|
|
err = RtlInitializeCriticalSection( myCritsec );
|
|
if (!NT_SUCCESS(err))
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, myCritsec);
|
|
return;
|
|
}
|
|
|
|
myCritsec = (CRITICAL_SECTION *) InterlockedExchangePointer( (PVOID *) &pCritsec, myCritsec );
|
|
if (myCritsec)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, myCritsec);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get this server's IPX address.
|
|
//
|
|
s = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );
|
|
if (s != -1)
|
|
{
|
|
size = sizeof(new_ipxaddr);
|
|
|
|
memset(&new_ipxaddr, 0, sizeof(new_ipxaddr));
|
|
new_ipxaddr.sa_family = AF_IPX;
|
|
|
|
err = bind(s, (struct sockaddr *)&new_ipxaddr, sizeof(new_ipxaddr));
|
|
if (err == 0)
|
|
{
|
|
err = getsockname(s, (struct sockaddr *)&new_ipxaddr, &size);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
err = -1;
|
|
}
|
|
|
|
if (err != 0)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: socket() or getsockname() failed %d, aborting SAP setup\n",
|
|
GetLastError()));
|
|
|
|
return;
|
|
}
|
|
|
|
if (s != -1)
|
|
{
|
|
closesocket(s);
|
|
}
|
|
|
|
EnterCriticalSection( pCritsec );
|
|
|
|
if (0 != memcmp( old_ipxaddr.sa_netnum, new_ipxaddr.sa_netnum, sizeof(old_ipxaddr.sa_netnum)) ||
|
|
0 != memcmp( old_ipxaddr.sa_nodenum, new_ipxaddr.sa_nodenum, sizeof(old_ipxaddr.sa_nodenum)))
|
|
{
|
|
memcpy( &old_ipxaddr, &new_ipxaddr, sizeof(old_ipxaddr) );
|
|
|
|
LeaveCriticalSection( pCritsec );
|
|
|
|
if (*((long *) &new_ipxaddr.sa_netnum) != IPX_BOGUS_NETWORK_NUMBER)
|
|
{
|
|
CallSetService( &new_ipxaddr, TRUE);
|
|
}
|
|
else
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: SPX net number is bogus. Not registering until a real address arrives. \n"));
|
|
|
|
CallSetService( &new_ipxaddr, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LeaveCriticalSection( pCritsec );
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
CallSetService(
|
|
SOCKADDR_IPX * pipxaddr,
|
|
BOOL fRegister
|
|
)
|
|
/*++
|
|
Function Name:CallSetService
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
DWORD ignore;
|
|
DWORD status;
|
|
|
|
// SetService params
|
|
WSAQUERYSETW info;
|
|
CSADDR_INFO addresses;
|
|
|
|
// GetComputerName parameters
|
|
static WCHAR buffer[MAX_COMPUTERNAME_LENGTH + 1];
|
|
static BOOL bufferValid = FALSE;
|
|
|
|
if (!bufferValid)
|
|
{
|
|
// Get this server's name
|
|
ignore = MAX_COMPUTERNAME_LENGTH + 1;
|
|
if (!GetComputerNameW(buffer, &ignore))
|
|
{
|
|
return;
|
|
}
|
|
bufferValid = TRUE;
|
|
}
|
|
|
|
// 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...
|
|
|
|
pipxaddr->sa_socket = htons(34280);
|
|
|
|
// Fill in the service info structure.
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
info.dwSize = sizeof(info);
|
|
info.lpszServiceInstanceName = buffer;
|
|
info.lpServiceClassId = (GUID *)&RPC_SAP_SERVICE_TYPE;
|
|
info.lpszComment = L"RPC Services";
|
|
info.dwNameSpace = NS_SAP;
|
|
info.dwNumberOfCsAddrs = 1;
|
|
info.lpcsaBuffer = &addresses;
|
|
|
|
addresses.LocalAddr.iSockaddrLength = sizeof(SOCKADDR_IPX);
|
|
addresses.LocalAddr.lpSockaddr = (LPSOCKADDR) pipxaddr;
|
|
addresses.RemoteAddr.iSockaddrLength = sizeof(SOCKADDR_IPX);
|
|
addresses.RemoteAddr.lpSockaddr = (LPSOCKADDR) pipxaddr;
|
|
addresses.iSocketType = AF_IPX;
|
|
addresses.iProtocol = NSPROTO_IPX;
|
|
|
|
status = WSASetService(&info,
|
|
fRegister ? RNRSERVICE_REGISTER : RNRSERVICE_DEREGISTER,
|
|
0);
|
|
|
|
ASSERT(status == SOCKET_ERROR || status == 0);
|
|
if (status == SOCKET_ERROR)
|
|
{
|
|
status = GetLastError();
|
|
}
|
|
|
|
if (status == 0)
|
|
{
|
|
if (fRegister)
|
|
{
|
|
SapState = SapStateEnabled;
|
|
}
|
|
else
|
|
{
|
|
SapState = SapStateDisabled;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: WSASetService(%s) failed %d\n",
|
|
fRegister ? "ENABLE" : "DISABLE",
|
|
status));
|
|
}
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
|
|
extern void
|
|
DealWithDeviceEvent();
|
|
|
|
|
|
void RPC_ENTRY
|
|
UpdateAddresses( PVOID arg )
|
|
{
|
|
// Calls to this function are serialized
|
|
DealWithDeviceEvent();
|
|
}
|
|
|