windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/wkssvc/server/message.c

654 lines
17 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991-1992 Microsoft Corporation
Module Name:
message.c
Abstract:
This module contains the worker routines for the NetMessageBufferSend
API implemented in the Workstation service.
Author:
Rita Wong (ritaw) 29-July-1991
Revision History:
Terence Kwan (terryk) 20-Oct-1993
Initialize the system inside NetrMessageBufferSend for the first send
--*/
#include "wsutil.h"
#include "wsconfig.h" // WsInfo.WsComputerName
#include "wsmsg.h" // Send worker routines
#include "wssec.h" // Security object
#include <lmwksta.h> // NetWkstaUserGetInfo
#include "msgsvcsend.h" // NetrSendMessage interface for internet direct send
STATIC
NET_API_STATUS
WsGetSenderName(
OUT LPTSTR Sender
);
STATIC
DWORD
WsSendInternetMessage(
IN LPTSTR MessageName,
IN LPTSTR To,
IN LPTSTR Sender,
IN LPBYTE Message,
IN DWORD MessageSize
);
STATIC
NET_API_STATUS
WsSendDirectedMessage(
IN LPTSTR To,
IN LPTSTR Sender,
IN LPBYTE Message,
IN DWORD MessageSize
);
NET_API_STATUS
NetrMessageBufferSend (
IN LPTSTR ServerName,
IN LPTSTR MessageName,
IN LPTSTR FromName OPTIONAL,
IN LPBYTE Message,
IN DWORD MessageSize
)
/*++
Routine Description:
This function is the NetMessageBufferSend entry point in the
Workstation service.
Arguments:
ServerName - Supplies the name of server to execute this function
MessageName - Supplies the message alias to send the message to.
FromName - Supplies the message alias of sender. If NULL, the sender
alias will default to the currently logged on user.
Message - Supplies a pointer to the message to send.
MessageSize - Supplies the size of the message in number of bytes.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status;
int i;
TCHAR Sender[UNLEN + 1];
TCHAR To[UNLEN + 1];
LPTSTR Asterix = NULL;
NTSTATUS ntstatus;
UNICODE_STRING UnicodeMessage;
OEM_STRING OemMessage;
static BOOL fInitialize = FALSE;
// Initialize the system if this this the first time.
if ( !fInitialize )
{
if (( ntstatus = WsInitializeMessageSend( TRUE /* first time */)) != NERR_Success )
{
return(ntstatus);
}
fInitialize = TRUE;
}
UNREFERENCED_PARAMETER(ServerName);
IF_DEBUG(MESSAGE) {
NetpKdPrint(("[Wksta] NetMessageBufferSend MessageSize=%lu\n",
MessageSize));
}
//
// Any local user, and domain admins and operators are allowed to
// send messages. Remote users besides domain admins, and operators
// are denied access.
//
if (NetpAccessCheckAndAudit(
WORKSTATION_DISPLAY_NAME, // Subsystem name
(LPTSTR) MESSAGE_SEND_OBJECT, // Object type name
MessageSendSd, // Security descriptor
WKSTA_MESSAGE_SEND, // Desired access
&WsMessageSendMapping // Generic mapping
) != NERR_Success) {
return ERROR_ACCESS_DENIED;
}
if (! ARGUMENT_PRESENT(FromName)) {
//
// Get the caller's username
//
if ((status = WsGetSenderName(Sender)) != NERR_Success) {
return status;
}
}
else {
//
// Insure we don't overwrite our buffer.
//
if (STRLEN(FromName) > UNLEN) {
STRNCPY(Sender, FromName, UNLEN);
FromName[UNLEN] = TCHAR_EOS;
}
else {
STRCPY(Sender, FromName);
}
}
//
// Convert the Unicode message to OEM character set (very similar
// to ANSI)
//
UnicodeMessage.Buffer = (PWCHAR) Message;
UnicodeMessage.Length = (USHORT) MessageSize;
UnicodeMessage.MaximumLength = (USHORT) MessageSize;
ntstatus = RtlUnicodeStringToOemString(
&OemMessage,
&UnicodeMessage,
TRUE
);
if (! NT_SUCCESS(ntstatus)) {
NetpKdPrint(("[Wksta] NetrMessageBufferSend: RtlUnicodeStringToOemString failed "
FORMAT_NTSTATUS "\n", ntstatus));
return NetpNtStatusToApiStatus(ntstatus);
}
//
// If message name is longer than the maximum name length,
// truncate the name. Since DNLEN is way less than UNLEN,
// this will hold <DomainName*> if need be.
//
if (STRLEN(MessageName) > UNLEN)
{
STRNCPY(To, MessageName, UNLEN);
To[UNLEN] = TCHAR_EOS;
}
else
{
STRCPY(To, MessageName);
}
//
// Remove any trailing blanks from the "To" Name.
//
for (i = STRLEN(To) - 1; i >= 0; i--)
{
if (To[i] != TEXT(' '))
{
To[i + 1] = TEXT('\0');
break;
}
}
//
// Don't allow broadcasts anymore.
//
if (STRNCMP(To, TEXT("*"), 2) == 0)
{
status = ERROR_INVALID_PARAMETER;
goto CleanExit;
}
//
// Send message to a domain. Recipient name should be in the form of
// "DomainName*".
//
Asterix = STRRCHR(To, TCHAR_STAR);
if ((Asterix) && (*(Asterix + 1) == TCHAR_EOS)) {
*Asterix = TCHAR_EOS; // Overwrite trailing '*'
//
// If message size is too long to fit into a mailslot message,
// truncate it.
//
if (OemMessage.Length > MAX_GROUP_MESSAGE_SIZE) {
if ((status = WsSendToGroup(
To,
Sender,
OemMessage.Buffer,
MAX_GROUP_MESSAGE_SIZE
)) == NERR_Success) {
status = NERR_TruncatedBroadcast;
goto CleanExit;
}
} else {
status = WsSendToGroup(
To,
Sender,
OemMessage.Buffer,
(WORD) OemMessage.Length
);
goto CleanExit;
}
}
//
// Send a directed message
//
if (Asterix) {
RtlFreeOemString(&OemMessage);
return NERR_NameNotFound;
}
status = WsSendDirectedMessage(
To,
Sender,
OemMessage.Buffer,
OemMessage.Length
);
//
// If error suggests adapters have changed, reinitialize and try again
//
if (status == NERR_NameNotFound) {
NET_API_STATUS status1;
(void) WsInitializeMessageSend( FALSE /* second time */ );
status1 = WsSendDirectedMessage(
To,
Sender,
OemMessage.Buffer,
OemMessage.Length
);
// If this time we are successful, update final status
if (status1 == NERR_Success) {
status = NERR_Success;
}
}
//
// If error suggests that Netbios could not resolve the name, or is
// not running, try sending an Internet message.
//
if (status == NERR_NameNotFound) {
ntstatus = WsSendInternetMessage(
MessageName,
To,
Sender,
OemMessage.Buffer,
OemMessage.Length );
if (ntstatus == ERROR_SUCCESS) {
status = NERR_Success;
}
}
CleanExit:
RtlFreeOemString(&OemMessage);
return status;
}
STATIC
NET_API_STATUS
WsGetSenderName(
OUT LPTSTR Sender
)
/*++
Routine Description:
This function retrives the username of person who called
NetMessageBufferSend API. If the caller is not logged on, he/she has
no name; in this case, we return the computer name as the sender name.
Arguments:
Sender - Returns the username of the caller of the NetMessageBufferSend
API.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
//
// No username, sender is computer name
//
STRCPY(Sender, WsInfo.WsComputerName);
(VOID) I_NetNameCanonicalize(
NULL,
Sender,
Sender,
(UNLEN + 1) * sizeof(TCHAR),
NAMETYPE_MESSAGEDEST,
0
);
return NERR_Success;
}
STATIC
DWORD
WsSendInternetMessage(
IN LPTSTR MessageName,
IN LPTSTR To,
IN LPTSTR Sender,
IN LPBYTE Message,
IN DWORD MessageSize
)
/*++
Routine Description:
This routine sends the message to the computer specified by the MessageName. Note that the
MessageName must be resolvable using "gethostbyname", that is usernames or other general
net names are not supported for this type of send.
Arguments:
MessageName - The target name
To - Target name truncated to 16 characters
Sender - Sending computer name
Message -
MessageSize -
Return Value:
DWORD -
--*/
{
DWORD status;
LPSTR ansiTo = NULL;
LPSTR ansiSender = NULL;
LPSTR newMessage = NULL;
LPTSTR pszStringBinding = NULL;
RPC_BINDING_HANDLE hRpcBinding = NULL;
IF_DEBUG( MESSAGE )
NetpKdPrint(("[Wksta] WsSendInternet: enter, To %ws Sender %ws\n", To, Sender));
// Convert arguments to ansi
ansiTo = NetpAllocStrFromWStr( To );
if (ansiTo == NULL) {
IF_DEBUG( MESSAGE )
NetpKdPrint(("[Wksta] WsSendInternet: alloc to failed\n"));
status = ERROR_NOT_ENOUGH_MEMORY;
goto release;
}
ansiSender = NetpAllocStrFromWStr( Sender );
if (ansiSender == NULL) {
IF_DEBUG( MESSAGE )
NetpKdPrint(("[Wksta] WsSendInternet: alloc sender failed\n"));
status = ERROR_NOT_ENOUGH_MEMORY;
goto release;
}
newMessage = LocalAlloc( LMEM_FIXED, MessageSize + 1 );
if (newMessage == NULL) {
IF_DEBUG( MESSAGE )
NetpKdPrint(("[Wksta] WsSendInternet: alloc message failed\n"));
status = ERROR_NOT_ENOUGH_MEMORY;
goto release;
}
memcpy( newMessage, Message, MessageSize );
newMessage[MessageSize] = '\0';
// Treat the To argument as a computer name and try to bind
// Specify NULL endpoing, meaning bind to dynamic endpoint at call-time.
status = RpcStringBindingCompose( NULL, // UUID
TEXT("ncadg_ip_udp"), // pszProtocolSequence,
MessageName, // pszNetworkAddress,
NULL, // pszEndpoint,
NULL, // Options
&pszStringBinding);
if (status != ERROR_SUCCESS) {
IF_DEBUG( MESSAGE )
NetpKdPrint(("[Wksta] WsSendInternet: RpcStringBindingCompose failure: "
FORMAT_NTSTATUS "\n", status));
goto release;
}
status = RpcBindingFromStringBinding(pszStringBinding, &hRpcBinding);
if (status != ERROR_SUCCESS) {
IF_DEBUG( MESSAGE )
NetpKdPrint(("[Wksta] WsSendInternet: RpcBindingFromStringBinding failure: "
FORMAT_NTSTATUS "\n", status));
goto release;
}
status = NetrSendMessage( hRpcBinding, ansiSender, ansiTo, newMessage );
if (status != ERROR_SUCCESS) {
IF_DEBUG( MESSAGE ) {
NetpKdPrint(("[Wksta] WsSendInternet: NetrSendMessage failure: "
FORMAT_NTSTATUS "\n", status));
}
}
release:
if (ansiTo != NULL)
NetApiBufferFree( ansiTo );
if (ansiSender != NULL)
NetApiBufferFree( ansiSender );
if (newMessage != NULL)
LocalFree( newMessage );
if (pszStringBinding != NULL) {
RpcStringFree( &pszStringBinding ); // remote calls done; unbind
}
if (hRpcBinding != NULL) {
RpcBindingFree( &hRpcBinding ); // remote calls done; unbind
}
IF_DEBUG( MESSAGE ) {
NetpKdPrint(("[Wksta] WsSendInternet: exit, ntstatus= %d\n", status));
}
return status;
} /* WsSendInternetMessage */
STATIC
NET_API_STATUS
WsSendDirectedMessage(
IN LPTSTR To,
IN LPTSTR Sender,
IN LPBYTE Message,
IN DWORD MessageSize
)
/*++
Routine Description:
This function sends the specified message as a directed message
to the specified recipient. A call to the recipient is sent
out on each LAN adapter. If there is no response we try the
next LAN adapter until we hear from the targeted recipient.
Arguments:
To - Supplies the message alias of the recipient.
Sender - Supplies the message alias of sender.
Message - Supplies a pointer to the message to send.
MessageSize - Supplies the size of the message in number of bytes.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status = NERR_NameNotFound;
UCHAR i;
BOOL NameFound = FALSE;
UCHAR SessionNumber;
short MessageId;
//
// Try each network until someone answers the call. Only the first name
// found will receive the message. The same name on any other network
// will never see the message. This is to remain consistent with all
// other session based algorithms in LAN Man.
//
for (i = 0; i < WsNetworkInfo.LanAdapterNumbers.length; i++) {
//
// Attempt to establish a session
//
if ((status = NetpNetBiosCall(
WsNetworkInfo.LanAdapterNumbers.lana[i],
To,
Sender,
&SessionNumber
)) == NERR_Success) {
NameFound = TRUE;
IF_DEBUG(MESSAGE) {
NetpKdPrint(("[Wksta] Successfully called %ws\n", To));
}
if (MessageSize <= MAX_SINGLE_MESSAGE_SIZE) {
//
// Send single block message if possible
//
status = WsSendSingleBlockMessage(
WsNetworkInfo.LanAdapterNumbers.lana[i],
SessionNumber,
To,
Sender,
Message,
(WORD) MessageSize
);
}
else {
//
// Message too long, got to send multi-block message
//
//
// Send the begin message
//
if ((status = WsSendMultiBlockBegin(
WsNetworkInfo.LanAdapterNumbers.lana[i],
SessionNumber,
To,
Sender,
&MessageId
)) == NERR_Success) {
//
// Send the body of the message in as many blocks as necessary
//
for (; MessageSize > MAX_SINGLE_MESSAGE_SIZE;
Message += MAX_SINGLE_MESSAGE_SIZE,
MessageSize -= MAX_SINGLE_MESSAGE_SIZE) {
if ((status = WsSendMultiBlockText(
WsNetworkInfo.LanAdapterNumbers.lana[i],
SessionNumber,
Message,
MAX_SINGLE_MESSAGE_SIZE,
MessageId
)) != NERR_Success) {
break;
}
}
if (status == NERR_Success && MessageSize > 0) {
//
// Send the remaining message body
//
status = WsSendMultiBlockText(
WsNetworkInfo.LanAdapterNumbers.lana[i],
SessionNumber,
Message,
(WORD) MessageSize,
MessageId
);
}
//
// Send the end message
//
if (status == NERR_Success) {
status = WsSendMultiBlockEnd(
WsNetworkInfo.LanAdapterNumbers.lana[i],
SessionNumber,
MessageId
);
}
}
}
(VOID) NetpNetBiosHangup(
WsNetworkInfo.LanAdapterNumbers.lana[i],
SessionNumber
);
} // Call successful
if (NameFound) {
break;
}
}
return status;
}