654 lines
17 KiB
C
654 lines
17 KiB
C
/*++
|
||
|
||
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;
|
||
}
|