windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/msgsvc/server/msgapi.c
2020-09-26 16:20:57 +08:00

1599 lines
45 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
msgapi.c
Abstract:
Provides API functions for the messaging system.
Author:
Dan Lafferty (danl) 23-Jul-1991
Environment:
User Mode -Win32
Notes:
optional-notes
Revision History:
02-Sep-1993 wlees
Provide synchronization between rpc routines and Pnp reconfiguration
13-Jan-1993 danl
NetrMessageNameGetInfo: Allocation size calculation was incorrectly
trying to take the sizeof((NCBNAMSZ+1)*sizeof(WCHAR)). NCBNAMSZ is
a #define constant value.
22-Jul-1991 danl
Ported from LM2.0
--*/
//
// Includes
//
#include "msrv.h"
#include <tstring.h> // Unicode string macros
#include <lmmsg.h>
#include <netlib.h> // UNUSED macro
#include <netlibnt.h> // NetpNtStatusToApiStatus
#include <msgrutil.h> // NetpNetBiosReset
#include <rpc.h>
#include <msgsvc.h> // MIDL generated header file
#include "msgdbg.h" // MSG_LOG
#include "heap.h"
#include "msgdata.h"
#include "apiutil.h"
#include "msgsec.h" // Messenger Security Information
#include "msgsvcsend.h" // Broadcast message send interface
// Static data descriptor strings for remoting the Message APIs
static char nulstr[] = "";
NET_API_STATUS
NetrMessageNameEnum(
IN LPWSTR ServerName,
IN OUT LPMSG_ENUM_STRUCT InfoStruct,
IN DWORD PrefMaxLen,
OUT LPDWORD TotalEntries,
IN OUT LPDWORD ResumeHandle OPTIONAL
)
/*++
Routine Description:
This function provides information about the message service name table
at two levels of detail.
Arguments:
ServerName - Pointer to a string containing the name of the computer
that is to execute the API function.
InfoStruct - Pointer to a structure that contains the information that
RPC needs about the returned data. This structure contains the
following information:
Level - The desired information level - indicates how to
interpret the structure of the returned buffer.
EntriesRead - Indicates how many elements are returned in the
array of structures that are returned.
BufferPointer - Location for the pointer to the array of
structures that are being returned.
PrefMaxLen - Indicates a maximum size limit that the caller will allow
for the return buffer.
TotalEntries - Pointer to a value that upon return indicates the total
number of entries in the "active" database.
ResumeHandle - Inidcates where in the linked list to start the
enumeration. This is an optional parameter and can be NULL.
Return Value:
NERR_Success - The operation was successful. EntriesRead is valid.
ERROR_INVALID_LEVEL - An invalid info level was passed in.
ERROR_MORE_DATA - Not all the information in the database could be
returned due to the limititation placed on buffer size by
PrefMaxLen. One or more information records will be found in
the buffer. EntriesRead is valid.
ERROR_SERVICE_NOT_ACTIVE - The service is stopping.
NERR_BufTooSmall - The limitation (PrefMaxLen) on buffer size didn't
allow any information to be returned. Not even a single record
could be fit in a buffer that small.
NERR_InternalError - A name in the name table could not be translated
from ansi characters to unicode characters. (Note: this
currently causes 0 entries to be returned.)
--*/
{
DWORD hResume = 0; // resume handle value
DWORD entriesRead = 0;
DWORD retBufSize;
LPBYTE infoBuf;
LPBYTE infoBufTemp;
LPBYTE stringBuf;
DWORD entry_length; // Length of one name entry in buf
DWORD i,j,k; // index for name loop and flags
NET_API_STATUS status=0;
DWORD neti; // net index
ULONG SessionId = 0; // client session Id
DWORD dwMsgrState = GetMsgrState();
UNUSED (ServerName);
if (dwMsgrState == STOPPING || dwMsgrState == STOPPED) {
return ERROR_SERVICE_NOT_ACTIVE;
}
//
// Synchronize with Pnp configuration routine
//
MsgConfigurationLock(MSG_GET_SHARED,"NetrMessageNameEnum");
//
// If ResumeHandle is present and valid, initialize it.
//
if (ARGUMENT_PRESENT(ResumeHandle) && (*ResumeHandle < NCBMAX(0))) {
hResume = *ResumeHandle;
}
//
// In Hydra case, the display thread never goes asleep.
//
if (!g_IsTerminalServer)
{
//
// Wakeup the display thread so that any queue'd messages can be
// displayed.
//
MsgDisplayThreadWakeup();
}
//
// Initialize some of the return counts.
//
*TotalEntries = 0;
//
// API security check. This call can be called by anyone locally,
// but only by admins in the remote case.
//
status = NetpAccessCheckAndAudit(
SERVICE_MESSENGER, // Subsystem Name
(LPWSTR)MESSAGE_NAME_OBJECT, // Object Type Name
MessageNameSd, // Security Descriptor
MSGR_MESSAGE_NAME_ENUM, // Desired Access
&MsgMessageNameMapping); // Generic Mapping
if (status != NERR_Success) {
MSG_LOG(TRACE,
"NetrMessageNameEnum:NetpAccessCheckAndAudit FAILED %X\n",
status);
status = ERROR_ACCESS_DENIED;
goto exit;
}
if (g_IsTerminalServer)
{
// get the client session id
status = MsgGetClientSessionId(&SessionId);
if (status != NERR_Success) {
MSG_LOG(TRACE,
"NetrMessageNameEnum:Could not get client session Id \n",0);
goto exit;
}
}
//
// Determine the size of one element in the returned array.
//
switch( InfoStruct->Level) {
case 0:
if (InfoStruct->MsgInfo.Level0 == NULL)
{
status = ERROR_INVALID_PARAMETER;
goto exit;
}
entry_length = sizeof(MSG_INFO_0);
break;
case 1:
if (InfoStruct->MsgInfo.Level0 == NULL)
{
status = ERROR_INVALID_PARAMETER;
goto exit;
}
entry_length = sizeof(MSG_INFO_1);
break;
default:
status = ERROR_INVALID_LEVEL;
goto exit;
}
//
// Allocate enough space for return buffer
//
if (PrefMaxLen == -1) {
//
// If the caller has not specified a size, calculate a size
// that will hold the entire enumeration.
//
retBufSize =
((NCBMAX(0) * ((NCBNAMSZ+1) * sizeof(WCHAR))) + // max possible num strings
(NCBMAX(0) * entry_length)); // max possible num structs
}
else {
retBufSize = PrefMaxLen;
}
infoBuf = (LPBYTE)MIDL_user_allocate(retBufSize);
if (infoBuf == NULL) {
status = ERROR_NOT_ENOUGH_MEMORY;
goto exit;
}
stringBuf = infoBuf + (retBufSize & ~1); // & ~1 to align Unicode strings
//
// Block until data free
//
MsgDatabaseLock(MSG_GET_EXCLUSIVE,"NetrMessageNameEnum");
//
// Now copy as many names from the shared data name table as will fit
// into the callers buffer. The shared data is locked so that the name
// table can not change while it is being copied (eg by someone
// deleting a forwarded name on this station after the check for a valid
// name has been made but before the name has been read). The level 1
// information is not copied in this loop as it requires network
// activity which must be avoided while the shared data is locked.
//
//
// HISTORY:
//
// The original LM2.0 code looked at the names on all nets, and
// threw away duplicate ones. This implies that a name may appear
// on one net and not on another. Although, this can never happen if
// the names are always added via NetMessageNameAdd since that API
// will not add the name unless it can be added to all nets. However,
// forwarded names are added via a network receive, and may be added
// from one net only.
//
// Since NT is not supporting forwarding, it is no longer necessary to
// check each net. Since the only way to add names if via NetServiceAdd,
// this will assure that the name listed for one network are the same
// as the names listed for the others.
//
infoBufTemp = infoBuf;
neti=j=0;
status = NERR_Success;
for(i=hResume; (i<NCBMAX(neti)) && (status==NERR_Success); ++i) {
if (!(SD_NAMEFLAGS(neti,i) & (NFDEL | NFDEL_PENDING))) {
//
// in HYDRA case, we also consider the SessionId
//
if ((g_IsTerminalServer) && (!(MsgIsSessionInList(&(SD_SIDLIST(neti,i)), SessionId )))) {
continue;
}
//
// If a name is found we put it in the buffer if the
// following conditions are met. If we are processing
// the first net's names, put it in, it cannot be a
// duplicate. Otherwise, only put it in if it is not
// a duplicate of a name that is already in the user
// buffer.
// (NT_NOTE: duplicate names cannot occur).
//
//
// translate the name to unicode and put it into the buffer
//
status = MsgGatherInfo (
InfoStruct->Level,
SD_NAMES(neti,i),
&infoBufTemp,
&stringBuf);
if (status == NERR_Success) {
entriesRead++;
hResume++;
}
}
}
//
// Calculate the total number of entries by seeing how many names are
// left in the table and adding that to the entries read.
//
if (status == ERROR_NOT_ENOUGH_MEMORY) {
status = ERROR_MORE_DATA;
for (k=0; i < NCBMAX(neti); i++) {
if(!(SD_NAMEFLAGS(neti,i) & (NFDEL | NFDEL_PENDING))) {
k++;
}
}
*TotalEntries = k;
}
*TotalEntries += entriesRead;
//
// Free up the shared data table
//
MsgDatabaseLock(MSG_RELEASE,"NetrMessageNameEnum");
//
// If some unexpected error occured, ( couldn't unformat the name
// - or a bogus info level was passed in), then return the error.
//
if ( ! ((status == NERR_Success) || (status == ERROR_MORE_DATA)) ) {
MIDL_user_free(infoBuf);
infoBuf = NULL;
entriesRead = 0;
hResume = 0;
goto exit;
}
//
// if there were no entries read then either there were no more
// entries in the table, or the resume number was bogus.
// In this case, we want to free the allocated buffer storage.
//
if (entriesRead == 0) {
MIDL_user_free(infoBuf);
infoBuf = NULL;
entriesRead = 0;
hResume = 0;
status = NERR_Success;
if (*TotalEntries > 0) {
status = NERR_BufTooSmall;
}
}
//
// If we have finished enumerating everything, reset the resume
// handle to start at the beginning next time.
//
if (entriesRead == *TotalEntries) {
hResume = 0;
}
//
// Load up the information to return
//
switch(InfoStruct->Level) {
case 0:
InfoStruct->MsgInfo.Level0->EntriesRead = entriesRead;
InfoStruct->MsgInfo.Level0->Buffer = (PMSG_INFO_0)infoBuf;
break;
case 1:
InfoStruct->MsgInfo.Level0->EntriesRead = entriesRead;
InfoStruct->MsgInfo.Level0->Buffer = (PMSG_INFO_0)infoBuf;
break;
default:
//
// This was checked above
//
ASSERT(FALSE);
}
if (ARGUMENT_PRESENT(ResumeHandle)) {
*ResumeHandle = hResume;
}
exit:
MsgConfigurationLock(MSG_RELEASE,"NetrMessageNameEnum");
return (status);
}
NET_API_STATUS
NetrMessageNameGetInfo(
IN LPWSTR ServerName, // unicode server name, NULL if local
IN LPWSTR Name, // Ptr to asciz name to query
IN DWORD Level, // Level of detail requested
OUT LPMSG_INFO InfoStruct // Ptr to buffer for info
)
/*++
Routine Description:
This funtion provides forwarding information about a known message server
name table entry. However, since we do not support forwarding in NT,
this API is totally useless. We'll support it anyway though for
compatibility purposes.
Arguments:
ServerName - Pointer to a string containing the name of the computer
that is to execute the API function.
Name - The Messaging name that we are to get info on.
Level - The level of information desired
InfoStruct - Pointer to a location where the pointer to the returned
information structure is to be placed.
Return Value:
--*/
{
NET_API_STATUS status=NERR_Success;
LPMSG_INFO_0 infoBuf0;
LPMSG_INFO_1 infoBuf1;
CHAR formattedName[NCBNAMSZ];
ULONG SessionId = 0; // Client Session Id
DWORD dwMsgrState = GetMsgrState();
UNUSED (ServerName);
if (dwMsgrState == STOPPING || dwMsgrState == STOPPED)
{
return ERROR_SERVICE_NOT_ACTIVE;
}
if (MsgIsValidMsgName(Name) != 0)
{
return ERROR_INVALID_NAME;
}
//
// Synchronize with Pnp configuration routine
//
MsgConfigurationLock(MSG_GET_SHARED,"NetrMessageNameGetInfo");
//
// In Hydra case, the display thread never goes asleep.
//
if (!g_IsTerminalServer)
{
//
// Wakeup the display thread so that any queue'd messages can be
// displayed.
//
MsgDisplayThreadWakeup();
}
//
// API security check. This call can be called by anyone locally,
// but only by admins in the remote case.
//
status = NetpAccessCheckAndAudit(
SERVICE_MESSENGER, // Subsystem Name
(LPWSTR)MESSAGE_NAME_OBJECT, // Object Type Name
MessageNameSd, // Security Descriptor
MSGR_MESSAGE_NAME_INFO_GET, // Desired Access
&MsgMessageNameMapping); // Generic Mapping
if (status != NERR_Success) {
MSG_LOG(TRACE,
"NetrMessageNameGetInfo:NetpAccessCheckAndAudit FAILED %X\n",
status);
status = ERROR_ACCESS_DENIED;
goto exit;
}
//
// Format the name so it matches what is stored in the name table.
//
status = MsgFmtNcbName(formattedName, Name, NAME_LOCAL_END);
if (status != NERR_Success) {
MSG_LOG(ERROR,"NetrMessageGetInfo: could not format name\n",0);
status = NERR_NotLocalName;
goto exit;
}
status = NERR_Success;
//
// Look for the name in the shared data name array. (1st net only).
//
if (g_IsTerminalServer)
{
// get the client session id
status = MsgGetClientSessionId(&SessionId);
if (status != NERR_Success) {
MSG_LOG(ERROR,"NetrMessageGetInfo: could not get session id\n",0);
goto exit;
}
}
// look for the name in the database
if (MsgLookupNameForThisSession(0, formattedName, SessionId) == -1) {
MSG_LOG(ERROR,"NetrMessageGetInfo: Name not in table\n",0);
status = NERR_NotLocalName;
goto exit;
}
//
// Allocate storage for the returned buffer, and fill it in.
//
switch(Level) {
case 0:
infoBuf0 = (LPMSG_INFO_0)MIDL_user_allocate(
sizeof(MSG_INFO_0) + ((NCBNAMSZ+1)*sizeof(WCHAR)));
if (infoBuf0 == NULL) {
MSG_LOG(ERROR,
"NetrMessageNameGetInfo MIDL allocate FAILED %X\n",
GetLastError());
status = ERROR_NOT_ENOUGH_MEMORY;
goto exit;
}
//
// copy the name and set the pointer in the structure to point
// to it.
//
STRCPY((LPWSTR)(infoBuf0 + 1), Name);
infoBuf0->msgi0_name = (LPWSTR)(infoBuf0 + 1);
(*InfoStruct).MsgInfo0 = infoBuf0;
break;
case 1:
infoBuf1 = (LPMSG_INFO_1)MIDL_user_allocate(
sizeof(MSG_INFO_1) + ((NCBNAMSZ+1)*sizeof(WCHAR)) );
if (infoBuf1 == NULL) {
MSG_LOG(ERROR,
"NetrMessageNameGetInfo MIDL allocate FAILED %X\n",
GetLastError());
status = ERROR_NOT_ENOUGH_MEMORY;
goto exit;
}
//
// Copy the name, update pointers, and set forward info fields.
//
STRCPY((LPWSTR)(infoBuf1 + 1), Name);
infoBuf1->msgi1_name = (LPWSTR)(infoBuf1 + 1);
infoBuf1->msgi1_forward_flag = 0;
infoBuf1->msgi1_forward = NULL;
(*InfoStruct).MsgInfo1 = infoBuf1;
break;
default:
status = ERROR_INVALID_LEVEL;
goto exit;
}
status = NERR_Success;
exit:
MsgConfigurationLock(MSG_RELEASE,"NetrMessageNameGetInfo");
return status;
}
NET_API_STATUS
NetrMessageNameAdd(
LPWSTR ServerName, // NULL = local
LPWSTR Name // Pointer to name to add.
)
/*++
Routine Description:
This function performs a security check for all calls to this
RPC interface. Then it adds a new name to the Message
Server's name table by calling the MsgAddName function.
Arguments:
ServerName - Pointer to a string containing the name of the computer
that is to execute the API function.
Name - A pointer to the name to be added.
Return Value:
NERR_Success - The operation was successful.
ERROR_ACCESS_DENIED - If the Security Check Failed.
ERROR_SERVICE_NOT_ACTIVE - The service is stopping
Assorted Error codes from MsgAddName.
--*/
{
NET_API_STATUS status=0;
ULONG SessionId = 0;
DWORD dwMsgrState = GetMsgrState();
UNUSED (ServerName);
if (dwMsgrState == STOPPING || dwMsgrState == STOPPED) {
return ERROR_SERVICE_NOT_ACTIVE;
}
//
// Synchronize with Pnp configuration routine
//
MsgConfigurationLock(MSG_GET_SHARED,"NetrMessageNameAdd");
//
// API security check. This call can be called by anyone locally,
// but only by admins in the remote case.
//
status = NetpAccessCheckAndAudit(
SERVICE_MESSENGER, // Subsystem Name
(LPWSTR)MESSAGE_NAME_OBJECT, // Object Type Name
MessageNameSd, // Security Descriptor
MSGR_MESSAGE_NAME_ADD, // Desired Access
&MsgMessageNameMapping); // Generic Mapping
if (status != NERR_Success) {
MSG_LOG(TRACE,
"NetrMessageNameAdd:NetpAccessCheckAndAudit FAILED %X\n",
status);
status = ERROR_ACCESS_DENIED;
goto exit;
}
//
// In Hydra case, the display thread never goes asleep.
//
if (!g_IsTerminalServer)
{
//
// Since a new user may have just logged on, we want to check to see if
// there are any messages to be displayd.
//
MsgDisplayThreadWakeup();
}
else
{
// get the client session id
status = MsgGetClientSessionId(&SessionId);
if (status != NERR_Success)
{
MSG_LOG(ERROR, "NetrMessageNameAdd: could not get client session id\n",0);
goto exit;
}
}
//
// Call the function that actually adds the name.
//
MSG_LOG(TRACE, "NetrMessageNameAdd: call MsgAddName for Session %x\n",SessionId);
status = MsgAddName(Name, SessionId);
exit:
MsgConfigurationLock(MSG_RELEASE,"NetrMessageNameAdd");
return status;
}
NET_API_STATUS
MsgAddName(
LPWSTR Name,
ULONG SessionId
)
/*++
Routine Description:
This function adds a new name to the Message Server's name table.
It is available to be called internally (from within the Messenger
service).
The task of adding a new name to the Message Server's name table consists
of verifying that a session can be established for a new name (Note: this
check is subject to failure in a multiprocessing environment, since the
state of the adapter may change between the time of the check and the time
of the attempt to establish a session), verifying that the name does not
already exist in the local name table, adding the name to the local adapter
via an ADD NAME net bios call, adding the name to the Message Server's name
table and marking it as new, waking up the Message Server using the wakeup
semaphore, and checking to see if messages for the new name have been
forwarded (if they have been forwarded, the value of the fwd_action
flag is used to determine the action to be taken).
SIDE EFFECTS
Calls the net bios. May modify the Message Server's shared data area.
May call DosSemClear() on the wakeup semaphore.
Arguments:
Name - A pointer to the name to be added.
Return Value:
NERR_Success - The operation was successful.
assorted errors.
--*/
{
NCB ncb; // Network control block
TCHAR namebuf[NCBNAMSZ+2]; // General purpose name buffer
UCHAR net_err=0; // Storage for net error codes
NET_API_STATUS err_code=0; // Storage for return error codes
DWORD neti,i,name_i; // Index
NET_API_STATUS status=0;
if (MsgIsValidMsgName(Name) != 0)
{
return ERROR_INVALID_NAME;
}
MSG_LOG(TRACE,"Attempting to add the following name: %ws\n",Name);
STRNCPY( namebuf, Name, NCBNAMSZ+1);
namebuf[NCBNAMSZ+1] = '\0';
//
// Initialize the NCB
//
clearncb(&ncb);
//
// Format the name for NetBios.
// This converts the Unicode string to ansi.
//
status = MsgFmtNcbName(ncb.ncb_name, namebuf, NAME_LOCAL_END);
if (status != NERR_Success) {
MSG_LOG(ERROR,"MsgAddName: could not format name\n",0);
return (ERROR_INVALID_NAME);
}
//
// Check if the local name already exists on any netcard
// in this machine. This check does not mean the name dosn't
// exist on some other machine on the network(s).
//
for ( neti = 0; neti < SD_NUMNETS(); neti++ ) {
//
// Gain access to the shared database.
//
MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
for( i = 0, err_code = 0; i < 10; i++) {
// check if this alias is not already existing for this session
name_i = MsgLookupNameForThisSession(neti, ncb.ncb_name, SessionId);
if ((name_i) == -1) {
break;
}
if( (SD_NAMEFLAGS(neti,name_i) & NFDEL_PENDING) && (i < 9)) {
//
// Delete is pending so wait for it
//
Sleep(500L);
}
else {
//
// Setup error code
//
err_code = NERR_AlreadyExists;
break;
}
}
MsgDatabaseLock(MSG_RELEASE,"MsgAddName");
if ( err_code == NERR_AlreadyExists ) {
break;
}
}
if( err_code == 0)
{
//
// Either the name was not forwarded or the fwd_action flag
// was set so go ahead and try to add the name to each net.
//
ncb.ncb_name[NCBNAMSZ - 1] = NAME_LOCAL_END;
//
// on each network
//
for ( neti = 0; neti < SD_NUMNETS(); neti++ ) {
//
// Gain access to the shared database.
//
MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
if (g_IsTerminalServer)
{
// before looking for an empty slot,
// check if this alias does not already exist for another session
for( i = 0; i < 10; i++)
{
name_i = MsgLookupName(neti, ncb.ncb_name);
if ((name_i != -1) && (SD_NAMEFLAGS(neti, name_i) & NFDEL_PENDING))
{
//
// Delete is pending so wait for it
//
Sleep(500L);
}
else
{
break;
}
}
if (name_i != -1)
{
if (SD_NAMEFLAGS(neti, name_i) & NFDEL_PENDING) // still there ?
{
err_code = NERR_InternalError; // what else can we do ?
MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
break;
}
// this alias already exists for another session, so just add the session id in the list
MSG_LOG(TRACE,"MsgAddName: Alias already existing. Just adding Session %x \n", SessionId);
MsgAddSessionInList(&(SD_SIDLIST(neti, name_i)), SessionId); //There is not much we can do if this call fails
MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
continue;
}
}
for(i = 0; i < NCBMAX(neti); ++i)
{
//
// Loop to find empty slot
//
if (SD_NAMEFLAGS(neti,i) & NFDEL)
{
//
// If empty slot found, Lock slot in table and
// end the search
//
SD_NAMEFLAGS(neti,i) = NFLOCK;
MSG_LOG2(TRACE,"MsgAddName: Lock slot %d in table "
"for net %d\n",i,neti);
break;
}
}
if ((i == NCBMAX(neti)) && (i < NCB_MAX_ENTRIES))
{
// We can add another NCB - must hold the lock.
PNCB_DATA pNcbDataNew;
PNET_DATA pNetData = GETNETDATA(neti);
if (pNcbDataNew = (PNCB_DATA) LocalAlloc(LMEM_ZEROINIT,
sizeof(NCB_DATA)))
{
// Initialize and lock the NCB
pNcbDataNew->Ncb.ncb_cmd_cplt = 0xff;
pNcbDataNew->NameFlags = NFLOCK;
// Add the new NCB to the list
pNetData->NcbList[i] = pNcbDataNew;
//
//create an empty session list
//
InitializeListHead(&(SD_SIDLIST(neti,i)));
pNetData->NumNcbs++; // This must be done last
}
else
{
err_code = ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Unlock the shared database
//
MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
if( i >= NCBMAX(neti))
{
//
// If no room in name table
//
err_code = NERR_TooManyNames;
}
else if (err_code == NERR_Success)
{
//
// Send ADDNAME
//
ncb.ncb_command = NCBADDNAME; // Add name (wait)
ncb.ncb_lana_num = GETNETLANANUM(neti);
MSG_LOG1(TRACE,"MsgNameAdd: Calling sendncb for lana #%d...\n",
GETNETLANANUM(neti));
if ((net_err = Msgsendncb(&ncb,neti)) == 0)
{
MSG_LOG(TRACE,"MsgAddName: sendncb returned SUCCESS\n",0);
//
// successful add - Get the Lock.
//
MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
//
// Copy the name to shared memory
//
MSG_LOG3(TRACE,"MsgAddName: copy name (%s)\n\tto "
"shared data table (net,loc)(%d,%d)\n",
ncb.ncb_name, neti, i);
memcpy(SD_NAMES(neti,i),ncb.ncb_name, NCBNAMSZ);
//
// Set the name no.
//
SD_NAMENUMS(neti,i) = ncb.ncb_num ;
//
// Set new name flag
//
SD_NAMEFLAGS(neti,i) = NFNEW;
if (g_IsTerminalServer)
{
// Add the session id in the list
MSG_LOG(TRACE,"MsgAddName: Alias created for Session %x \n", SessionId);
MsgAddSessionInList(&(SD_SIDLIST(neti, i)), SessionId);
// If this fails due to low memory, we would find the name in the list and messages will
// not get deliviered. Doesn't cause any crashes. This is the best we can do
}
//
// Unlock share table
//
MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
//
// START A SESSION for this name.
//
err_code = MsgNewName(neti,i);
if (err_code != NERR_Success) {
MSG_LOG(TRACE, "MsgAddName: A Session couldn't be "
"created for this name %d\n",err_code);
MSG_LOG(TRACE,"MsgAddName: Delete the name "
"that failed (%s)\n",ncb.ncb_name)
ncb.ncb_command = NCBDELNAME;
ncb.ncb_lana_num = GETNETLANANUM(i);
net_err = Msgsendncb( &ncb, i);
if (net_err != 0) {
MSG_LOG(ERROR,"MsgAddName: Delete name "
"failed %d - pretend it's deleted anyway\n",net_err);
}
//
// Re-mark slot empty
//
SD_NAMEFLAGS(neti,i) = NFDEL;
MSG_LOG2(TRACE,"MsgAddName: UnLock slot %d in table "
"for net %d\n",i,neti);
MSG_LOG(TRACE,"MsgAddName: Name Deleted\n",0)
}
else {
//
//
// Wakeup the worker thread for that network.
//
SetEvent(wakeupSem[neti]);
}
}
else {
//
// else set error code
//
MSG_LOG(TRACE,
"MsgAddName: sendncb returned FAILURE 0x%x\n",
net_err);
err_code = MsgMapNetError(net_err);
//
// Re-mark slot empty
//
SD_NAMEFLAGS(neti,i) = NFDEL;
MSG_LOG2(TRACE,"MsgAddName: UnLock slot %d in table "
"for net %d\n",i,neti);
}
}
if ( err_code != NERR_Success )
{
//
//Try to delete the add names that were successful
//
for ( i = 0; i < neti; i++ )
{
MsgDatabaseLock(MSG_GET_EXCLUSIVE,"MsgAddName");
// try to delete only the alias for this session
name_i = MsgLookupNameForThisSession(i,
(char far *)(ncb.ncb_name),
SessionId);
if (name_i == -1)
{
err_code = NERR_InternalError;
MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
break;
}
if (g_IsTerminalServer)
{
// in any case remove the reference to the session
MSG_LOG(TRACE,"MsgAddName: Removing Session %x from list\n", SessionId);
MsgRemoveSessionFromList(&(SD_SIDLIST(i, name_i)), SessionId);
}
MsgDatabaseLock(MSG_RELEASE, "MsgAddName");
// if it was the last session using this alias then delete the alias
if ((!g_IsTerminalServer) || (IsListEmpty(&(SD_SIDLIST(i, name_i)))))
{
MSG_LOG(TRACE,"MsgAddName: Session list empty. Deleting the alias \n", 0);
//
// Delete name from card.
// If this call fails, we can't do much about it.
//
MSG_LOG1(TRACE,"MsgAddName: Delete the name that failed "
"for lana #%d\n",GETNETLANANUM(i))
ncb.ncb_command = NCBDELNAME;
ncb.ncb_lana_num = GETNETLANANUM(i);
Msgsendncb( &ncb, i);
//
// Re-mark slot empty
//
SD_NAMEFLAGS(i,name_i) = NFDEL;
MSG_LOG2(TRACE,"MsgAddName: UnLock slot %d in table "
"for net %d\n",i,neti);
}
}
//
// If an add was unsuccessful, stop the loop
//
break;
} // end else (err_code != NERR_Success)
} // end add names to net loop
} // end if ( !err_cd )
MSG_LOG(TRACE,"MsgAddName: exit with err_code = %x\n",err_code);
return(err_code);
}
NET_API_STATUS
NetrMessageNameDel(
IN LPWSTR ServerName, // Blank = local, else remote.
IN LPWSTR Name // Pointer to name to be deleted
)
/*++
Routine Description:
This function deletes a name from the Message Server's name table.
This function is called to delete a name that has been added by the
user or by a remote computer via a Start Forwarding request to the
Message Server. The user has no way of specifying whether the given
name is an additional name or a forwarded name, but since forwarding
of messages to one's own computer is prohibited, both forms of the
name cannot exist on one machine (unless the message system has been
circumvented--a simple enough thing to do). The given name is looked
up in the shared data area, and, if it is found, a DELETE NAME net bios
call is issued. If this call is successful, then the Message Server
will remove the name from its name table in shared memory, so this
function does not have to do so.
SIDE EFFECTS
Calls the net bios. Accesses the shared data area.
Arguments:
ServerName - Pointer to a string containing the name of the computer
that is to execute the API function.
Name - A pointer to the name to be deleted.
Return Value:
NERR_Success - The operation was successful.
ERROR_SERVICE_NOT_ACTIVE - The service is stopping.
--*/
{
NCB ncb; // Network control block
DWORD flags; // Name flags
DWORD i; // Index into name table
DWORD neti; // Network Index
NET_API_STATUS end_result;
DWORD name_len;
UCHAR net_err;
ULONG SessionId = 0; // Client Session Id
DWORD dwMsgrState = GetMsgrState();
UNUSED (ServerName);
if (dwMsgrState == STOPPING || dwMsgrState == STOPPED)
{
return ERROR_SERVICE_NOT_ACTIVE;
}
if (MsgIsValidMsgName(Name) != 0)
{
return ERROR_INVALID_NAME;
}
//
// Synchronize with Pnp configuration routine
//
MsgConfigurationLock(MSG_GET_SHARED,"NetrMessageNameDel");
//
// In Hydra case, the display thread never goes asleep.
//
if (!g_IsTerminalServer)
{
//
// Wakeup the display thread so that any queue'd messages can be
// displayed.
//
MsgDisplayThreadWakeup();
}
//
// API security check. This call can be called by anyone locally,
// but only by admins in the remote case.
//
end_result = NetpAccessCheckAndAudit(
SERVICE_MESSENGER, // Subsystem Name
(LPWSTR) MESSAGE_NAME_OBJECT, // Object Type Name
MessageNameSd, // Security Descriptor
MSGR_MESSAGE_NAME_DEL, // Desired Access
&MsgMessageNameMapping); // Generic Mapping
if (end_result != NERR_Success)
{
MSG_LOG(ERROR,
"NetrMessageNameDel:NetpAccessCheckAndAudit FAILED %d\n",
end_result);
goto exit;
}
//
// Initialize the NCB
//
clearncb(&ncb);
//
// Format the username (this makes it non-unicode);
//
end_result = MsgFmtNcbName(ncb.ncb_name, Name, NAME_LOCAL_END);
if (end_result != NERR_Success)
{
MSG_LOG(ERROR,
"NetrMessageNameDel: could not format name %d\n",
end_result);
goto exit;
}
if (g_IsTerminalServer)
{
end_result = MsgGetClientSessionId(&SessionId);
if (end_result != NERR_Success)
{
MSG_LOG(ERROR,
"NetrMessageNameDel: could not get session id %d\n",
end_result);
goto exit;
}
}
end_result = NERR_Success;
//
// for all nets
//
for ( neti = 0; neti < SD_NUMNETS(); neti++ ) {
//
// Block until data free
//
MsgDatabaseLock(MSG_GET_EXCLUSIVE,"NetrMessageNameDel");
name_len = STRLEN(Name);
if((name_len > NCBNAMSZ)
||
((i = MsgLookupNameForThisSession( neti, ncb.ncb_name, SessionId))) == -1)
{
MSG_LOG(TRACE,"NetrMessageNameDel: Alias not found for Session %x \n", SessionId);
//
// No such name to delete - exit
//
MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
end_result = NERR_NotLocalName;
goto exit;
}
if (g_IsTerminalServer)
{
// remove the session id from the list
MSG_LOG(TRACE,"NetrMessageNameDel: Removing Session %x from list\n", SessionId);
MsgRemoveSessionFromList(&(SD_SIDLIST(neti,i)), SessionId);
}
//
// in Hydra case, if it is not the last session using this alias, do not delete the alias
//
if ((g_IsTerminalServer) && (!IsListEmpty(&(SD_SIDLIST(neti,i)))))
{
MSG_LOG(TRACE,"NetrMessageNameDel: Session list is not empty. Do not delete the alias\n", 0);
MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
continue;
}
else
{
MSG_LOG(TRACE,"NetrMessageNameDel: Session list is empty. Deleting the alias\n", 0);
}
flags = SD_NAMEFLAGS(neti,i);
if(!(flags & (NFMACHNAME | NFLOCK))
&&
!(flags & NFFOR))
{
//
// Show delete pending
//
SD_NAMEFLAGS(neti,i) |= NFDEL_PENDING;
}
MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
if (flags & NFMACHNAME)
{
//
// If name is computer name
//
end_result = NERR_DelComputerName;
goto exit;
}
if(flags & NFLOCK)
{
//
// If name is locked
//
end_result = NERR_NameInUse;
MSG_LOG(TRACE,"NetrMessageNameDel: Deleting a locked name is forbidden\n", 0);
goto exit;
}
//
// Delete the Name
//
ncb.ncb_command = NCBDELNAME; // Delete name (wait)
ncb.ncb_lana_num = GETNETLANANUM(neti);
if( (net_err = Msgsendncb( &ncb, neti)) != 0 )
{
MSG_LOG(ERROR,"NetrMessageNameDel:send NCBDELNAME failed 0x%x\n",
net_err);
//
// The name that has been marked as delete pending was not
// successfully deleted so now go through all the work of
// finding the name again (cannot even use the same index
// in case deleted by another process) and remove the
// Del pending flag
//
//
// Attempt to block until data free but don't stop
// the recovery if can not block the data
//
MsgDatabaseLock(MSG_GET_EXCLUSIVE,"NetrMessageNameDel");
i = MsgLookupName(neti,ncb.ncb_name);
if(i != -1)
{
SD_NAMEFLAGS(neti,i) &= ~NFDEL_PENDING;
if (g_IsTerminalServer)
{
MSG_LOG(TRACE,"NetrMessageNameDel: Unable to delete alias. Re-adding Session %x \n", SessionId);
// re-insert the session id in the list
MsgAddSessionInList(&(SD_SIDLIST(neti,i)), SessionId);
}
end_result = NERR_IncompleteDel; // Unable to delete name
}
else
{
//
// Another thread deleted this name while we were executing
// above, so this name no longer exists.
//
end_result = NERR_NotLocalName;
}
MsgDatabaseLock(MSG_RELEASE, "NetrMessageNameDel");
}
} // End for all nets
exit:
MsgConfigurationLock(MSG_RELEASE,"NetrMessageNameDel");
return(end_result);
}
DWORD
NetrSendMessage(
RPC_BINDING_HANDLE hRpcBinding,
LPSTR From,
LPSTR To,
LPSTR Text
)
/*++
Routine Description:
This is the RPC handler for the SendMessage RPC. It takes the arguments, transforms them, and
passes them to Msglogsbm for display.
Arguments:
None
Return Value:
None
--*/
{
DWORD length;
PCHAR newText;
DWORD dwMsgrState = GetMsgrState();
UNUSED(hRpcBinding);
if (dwMsgrState == STOPPING || dwMsgrState == STOPPED) {
return ERROR_SERVICE_NOT_ACTIVE;
}
MSG_LOG3(TRACE,
"NetrSendMessage, From '%s' To '%s' Text '%s'\n",
From, To, Text);
// Msglogsbm takes a wierd counted string argument with a short of length in the front.
// Whip one up
length = strlen( Text );
newText = LocalAlloc( LMEM_FIXED, length + 3 ); // newText should be aligned
if (newText == NULL)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
*((PUSHORT) newText) = (USHORT) length;
strcpy( newText + 2, Text );
// Display the message
if (g_IsTerminalServer)
{
Msglogsbm( From, To, newText, (ULONG)EVERYBODY_SESSION_ID );
}
else
{
Msglogsbm (From, To, newText, 0);
}
LocalFree( newText );
return STATUS_SUCCESS;
}
NET_API_STATUS
MsgGetClientSessionId(
OUT PULONG pSessionId
)
/*++
Routine Description:
This function gets the session id of the client thread.
Note: it should be called only on HYDRA context, never in regular NT
Arguments:
pSessionId -
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status;
NTSTATUS ntstatus;
HANDLE CurrentThreadToken;
ULONG SessionId;
ULONG ReturnLength;
ntstatus = RpcImpersonateClient(NULL);
if (ntstatus != RPC_S_OK)
{
MSG_LOG1(ERROR,
"MsgGetClientSessionId: RpcImpersonateClient FAILED %#x\n",
ntstatus);
return NetpNtStatusToApiStatus(ntstatus);
}
ntstatus = NtOpenThreadToken(
NtCurrentThread(),
TOKEN_QUERY,
TRUE, // Use messenger service's security context to open thread token
&CurrentThreadToken
);
if (! NT_SUCCESS(ntstatus)) // error
{
MSG_LOG(ERROR,"MsgGetClientSessionId : Cannot open the current thread token %08lx\n", ntstatus);
}
else // OK
{
//
// Get the session id of the client thread
//
ntstatus = NtQueryInformationToken(
CurrentThreadToken,
TokenSessionId,
&SessionId,
sizeof(ULONG),
&ReturnLength);
if (! NT_SUCCESS(ntstatus)) // Error
{
MSG_LOG(ERROR,
"MsgGetClientSessionId: Cannot query current thread's token %08lx\n",
ntstatus);
NtClose(CurrentThreadToken);
}
else // OK
{
NtClose(CurrentThreadToken);
*pSessionId = SessionId;
}
}
RpcRevertToSelf();
status = NetpNtStatusToApiStatus(ntstatus);
//
// temporary security to avoid any problem:
// if we cannot get the session id,
// assume it is for the console.
//
if (status != NERR_Success)
{
*pSessionId = 0;
status = NERR_Success;
}
return status;
}