/*++ 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 // 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 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; }