/*++ Copyright (c) 1991-92 Microsoft Corporation Module Name: msgrutil.c Abstract: This file contains functions that are used by the messenger service and its API as well as the NetMessageSend API. This module contains the following helper routines: NetpNetBiosReset NetpNetBiosAddName NetpNetBiosDelName NetpNetBiosGetAdapterNumbers NetpNetBiosCall NetpNetBiosHangup NetpNetBiosReceive NetpNetBiosSend NetpStringToNetBiosName NetpNetBiosStatusToApiStatus NetpSmbCheck These function prototypes can be found in net\inc\msgrutil.h. Authors: Rita Wong (ritaw) 26-July-1991 Dan Lafferty (danl) 26-July-1991 Revision History: 05-May-1992 JohnRo Quiet normal debug messages. Changed to use FORMAT_ equates for most things. Made changes suggested by PC-LINT. --*/ #include // NT definitions #include // NT runtime library definitions #include #include // Win32 constant definitions & error codes #include // LAN Manager common definitions #include // LAN Manager network error definitions #include // IF_DEBUG #include // NetpKdPrint(()), FORMAT_ equates. #include // needed for smb.h #include // Server Message Block definitions #include // NetBIOS 3.0 definitions #include // strlen #include // Function prototypes from this module #include // I_NetNameCanonicalize() #include // NetpAllocStrFromWStr(), STRLEN(), etc. #include // NetApiBufferFree(). static NET_API_STATUS NetpIssueCallWithRetries( IN PNCB CallNcb, IN UCHAR LanAdapterNumber ); NET_API_STATUS NetpNetBiosReset( IN UCHAR LanAdapterNumber ) /*++ Routine Description: This function resets LAN adapter. Arguments: LanAdapterNumber - Supplies the number of the LAN adapter. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NCB Ncb; UCHAR NcbStatus; RtlZeroMemory((PVOID) &Ncb, sizeof(NCB)); Ncb.ncb_command = NCBRESET; Ncb.ncb_lsn = 0; Ncb.ncb_callname[0] = 24; // Max Num Sessions Ncb.ncb_callname[1] = 0; Ncb.ncb_callname[2] = 16; // Max Num Names Ncb.ncb_callname[3] = 0; Ncb.ncb_lana_num = LanAdapterNumber; NcbStatus = Netbios(&Ncb); return NetpNetBiosStatusToApiStatus(NcbStatus); } NET_API_STATUS NetpNetBiosAddName( IN PCHAR NetBiosName, IN UCHAR LanAdapterNumber, OUT PUCHAR NetBiosNameNumber OPTIONAL ) /*++ Routine Description: This function adds a NetBIOS name to the specified LAN adapter. Arguments: NetBiosName - Supplies a NetBIOS name to be added. LanAdapterNumber - Supplies the number of the LAN adapter. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NCB Ncb; UCHAR NcbStatus; RtlZeroMemory((PVOID) &Ncb, sizeof(NCB)); Ncb.ncb_command = NCBADDNAME; memcpy(Ncb.ncb_name, NetBiosName, NCBNAMSZ); Ncb.ncb_lana_num = LanAdapterNumber; NcbStatus = Netbios(&Ncb); if (NcbStatus == NRC_GOODRET) { IF_DEBUG(NETBIOS) { Ncb.ncb_name[NCBNAMSZ - 1] = '\0'; NetpKdPrint(("[Netlib] Successfully added name " FORMAT_LPSTR ". " "Name number is " FORMAT_DWORD "\n", Ncb.ncb_name, (DWORD) Ncb.ncb_num)); } if (ARGUMENT_PRESENT(NetBiosNameNumber)) { *NetBiosNameNumber = Ncb.ncb_num; } return NERR_Success; } return NetpNetBiosStatusToApiStatus(NcbStatus); } NET_API_STATUS NetpNetBiosDelName( IN PCHAR NetBiosName, IN UCHAR LanAdapterNumber ) /*++ Routine Description: This function adds a NetBIOS name to the specified LAN adapter. Arguments: NetBiosName - Supplies a NetBIOS name to be added. LanAdapterNumber - Supplies the number of the LAN adapter. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NCB Ncb; UCHAR NcbStatus; RtlZeroMemory((PVOID) &Ncb, sizeof(NCB)); Ncb.ncb_command = NCBDELNAME; memcpy(Ncb.ncb_name, NetBiosName, NCBNAMSZ); Ncb.ncb_lana_num = LanAdapterNumber; NcbStatus = Netbios(&Ncb); if (NcbStatus == NRC_GOODRET) { IF_DEBUG(NETBIOS) { Ncb.ncb_name[NCBNAMSZ - 1] = '\0'; NetpKdPrint(("[Netlib] Successfully deleted name " FORMAT_LPSTR ". " "Name number is " FORMAT_DWORD "\n", Ncb.ncb_name, (DWORD) Ncb.ncb_num)); } return NERR_Success; } return NetpNetBiosStatusToApiStatus(NcbStatus); } NET_API_STATUS NetpNetBiosGetAdapterNumbers( OUT PLANA_ENUM LanAdapterBuffer, IN WORD LanAdapterBufferSize ) /*++ Routine Description: Arguments: LanAdapterBuffer - Returns LAN adapter numbers in this buffer. LanAdapterBufferSize - Supplies the size of the output buffer which LAN LAN adapter numbers will be written to. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NCB Ncb; RtlZeroMemory((PVOID) &Ncb, sizeof(NCB)); Ncb.ncb_command = NCBENUM; Ncb.ncb_buffer = (char FAR *) LanAdapterBuffer; Ncb.ncb_length = LanAdapterBufferSize; return NetpNetBiosStatusToApiStatus(Netbios(&Ncb)); } NET_API_STATUS NetpStringToNetBiosName( OUT PCHAR NetBiosName, IN LPTSTR String, IN DWORD CanonicalizeType, IN WORD Type ) /*++ Routine Description: This function converts a zero terminated string plus a specified NetBIOS name type into a 16-byte NetBIOS name: [ ANSI String ][ Space Padding ][Type] If the input string for the NetBIOS name is fewer than 15 characters, spaces will be used to pad the string to 15 characters. The 16th byte designates the type of the NetBIOS name. Input strings that are longer than 15 characters will be truncated to 15 characters. Arguments: NetBiosName - Returns the formatted NetBIOS name. String - Supplies a pointer to a zero terminated string. CanonicalizeType - Supplies a value to determine how the string should be canonicalized. Type - Supplies the type of the NetBIOS name. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS Status = NERR_Success; DWORD i; LPSTR SourceString; Status = I_NetNameCanonicalize( NULL, String, String, (STRLEN(String) + 1) * sizeof(TCHAR), CanonicalizeType, 0 ); if (Status != NERR_Success) { IF_DEBUG(NETBIOS) { NetpKdPrint(("[Netlib] Error canonicalizing message alias " FORMAT_LPSTR " " FORMAT_API_STATUS "\n", String, Status)); } return Status; } SourceString = NetpAllocStrFromWStr(String); if (SourceString == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } (VOID) strcpy(NetBiosName, SourceString); for (i = strlen(SourceString); i < (NCBNAMSZ - 1); i++) { NetBiosName[i] = ' '; } NetBiosName[NCBNAMSZ - 1] = (CHAR) Type; (VOID) NetApiBufferFree(SourceString); return Status; } NET_API_STATUS NetpNetBiosCall( IN UCHAR LanAdapterNumber, IN LPTSTR NameToCall, IN LPTSTR Sender, OUT UCHAR *SessionNumber ) /*++ Routine Description: This function opens a session with the specified name to call. Arguments: LanAdapterNumber - Supplies the number of the LAN adapter. NameToCall - Supplies the name to call. Sender - Supplies the name who makes the call. SessionNumber - Returns the session number of the session established. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS status; NCB Ncb; RtlZeroMemory((PVOID) &Ncb, sizeof(NCB)); if ((status = NetpStringToNetBiosName( Ncb.ncb_callname, NameToCall, NAMETYPE_MESSAGEDEST, MESSAGE_ALIAS_TYPE )) != NERR_Success) { return status; } if ((status = NetpStringToNetBiosName( Ncb.ncb_name, Sender, NAMETYPE_MESSAGEDEST, WKSTA_TO_MESSAGE_ALIAS_TYPE )) != NERR_Success) { return status; } Ncb.ncb_rto = 30; // Receives time out after 15 seconds Ncb.ncb_sto = 30; // Sends time out after 15 seconds Ncb.ncb_command = NCBCALL; // Call (wait) Ncb.ncb_lana_num = LanAdapterNumber; // // Issue the NetBIOS call with retries // if (NetpIssueCallWithRetries(&Ncb, LanAdapterNumber) != NERR_Success) { return NERR_NameNotFound; } *SessionNumber = Ncb.ncb_lsn; return NERR_Success; } NET_API_STATUS NetpNetBiosHangup( IN UCHAR LanAdapterNumber, IN UCHAR SessionNumber ) /*++ Routine Description: This function closes and opened session. Arguments: LanAdapterNumber - Supplies the number of the LAN adapter. SessionNumber - Supplies the session number of the session to close. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NCB Ncb; UCHAR NcbStatus; RtlZeroMemory((PVOID) &Ncb, sizeof(NCB)); Ncb.ncb_command = NCBHANGUP; Ncb.ncb_lana_num = LanAdapterNumber; Ncb.ncb_lsn = SessionNumber; NcbStatus = Netbios(&Ncb); if (NcbStatus == NRC_GOODRET) { IF_DEBUG(NETBIOS) { NetpKdPrint(("[Netlib] NetBIOS successfully hung up\n")); } return NERR_Success; } return NetpNetBiosStatusToApiStatus(NcbStatus); } NET_API_STATUS NetpNetBiosSend( IN UCHAR LanAdapterNumber, IN UCHAR SessionNumber, IN PCHAR SendBuffer, IN WORD SendBufferSize ) /*++ Routine Description: This function sends data in SendBuffer to the session partner specified by SessionNumber. Arguments: LanAdapterNumber - Supplies the number of the LAN adapter. SessionNumber - Supplies the session number of a session established with NetBIOS CALL and LISTEN commands. SendBuffer - Supplies a pointer to data to be sent. SendBufferSize - Supplies the size of the data in bytes. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NCB Ncb; UCHAR NcbStatus; RtlZeroMemory((PVOID) &Ncb, sizeof(NCB)); Ncb.ncb_command = NCBSEND; Ncb.ncb_lana_num = LanAdapterNumber; Ncb.ncb_lsn = SessionNumber; Ncb.ncb_buffer = SendBuffer; Ncb.ncb_length = SendBufferSize; NcbStatus = Netbios(&Ncb); if (NcbStatus == NRC_GOODRET) { IF_DEBUG(NETBIOS) { NetpKdPrint(("[Netlib] NetBIOS successfully sent data\n")); } return NERR_Success; } return NetpNetBiosStatusToApiStatus(NcbStatus); } NET_API_STATUS NetpNetBiosReceive( IN UCHAR LanAdapterNumber, IN UCHAR SessionNumber, OUT PUCHAR ReceiveBuffer, IN WORD ReceiveBufferSize, IN HANDLE EventHandle, OUT WORD *NumberOfBytesReceived ) /*++ Routine Description: This function posts a NetBIOS receive data request to the session partner specified by SessionNumber. Arguments: LanAdapterNumber - Supplies the number of the LAN adapter. SessionNumber - Supplies the session number of a session established with NetBIOS CALL and LISTEN commands. ReceiveBuffer - Returns the received data in this buffer. ReceiveBufferSize - Supplies the size of the receive buffer. EventHandle - Supplies a handle to a Win32 event which will be signalled when an ASYNCH receive command completes. If this value is zero, the receive command is synchronous. NumberOfBytesReceived - Returns the number of bytes of data received. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NCB Ncb; UCHAR NcbStatus; RtlZeroMemory((PVOID) &Ncb, sizeof(NCB)); Ncb.ncb_command = NCBRECV; Ncb.ncb_lana_num = LanAdapterNumber; Ncb.ncb_lsn = SessionNumber; Ncb.ncb_buffer = ReceiveBuffer; Ncb.ncb_length = ReceiveBufferSize; IF_DEBUG(NETBIOS) { NetpKdPrint(("[Netlib] ncb_length before receive is " FORMAT_WORD_ONLY "\n", (WORD) Ncb.ncb_length)); } Ncb.ncb_event = EventHandle; NcbStatus = Netbios(&Ncb); if (NcbStatus == NRC_GOODRET) { IF_DEBUG(NETBIOS) { NetpKdPrint(("[Netlib] NetBIOS successfully received data\n")); NetpKdPrint(("[Netlib] ncb_length after receive is " FORMAT_WORD_ONLY "\n", (WORD) Ncb.ncb_length)); } *NumberOfBytesReceived = Ncb.ncb_length; return NERR_Success; } return NetpNetBiosStatusToApiStatus(NcbStatus); } NET_API_STATUS NetpNetBiosStatusToApiStatus( UCHAR NetBiosStatus ) { IF_DEBUG(NETBIOS) { NetpKdPrint(("[Netlib] Netbios status is x%02x\n", NetBiosStatus)); } // // Slight optimization // if (NetBiosStatus == NRC_GOODRET) { return NERR_Success; } switch (NetBiosStatus) { case NRC_NORES: return NERR_NoNetworkResource; case NRC_DUPNAME: return NERR_AlreadyExists; case NRC_NAMTFUL: return NERR_TooManyNames; case NRC_ACTSES: return NERR_DeleteLater; case NRC_REMTFUL: return ERROR_REM_NOT_LIST; case NRC_NOCALL: return NERR_NameNotFound; case NRC_NOWILD: case NRC_NAMERR: return ERROR_INVALID_PARAMETER; case NRC_INUSE: case NRC_NAMCONF: return NERR_DuplicateName; default: return NERR_NetworkError; } } static NET_API_STATUS NetpIssueCallWithRetries( IN PNCB CallNcb, IN UCHAR LanAdapterNumber ) /*++ Routine Description: This function issues the NetBIOS call command with retries, just in case the receiving name is busy fielding a message at the moment. Arguments: CallNcb - Supplies a pointer to the NCB which has been initialized with the proper values to submit an NetBIOS call command. LanAdapterNumber - Supplies the number of the LAN adapter. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { #define NETP_MAX_CALL_RETRY 5 NCB Ncb; WORD RetryCount = 0; UCHAR NetBiosStatus; NB30_ADAPTER_STATUS StatusBuffer; // Adapter status buffer // // It is possible that the remote receiving name is present but is // currently receiving another message and so is not listening. // If the return code from the call is NRC_NOCALL then check to see if // the name is actually present by issuing an ASTAT call to the name. // If the ASTAT succeeds then sleep and retry the call. // NetBiosStatus = Netbios(CallNcb); while (NetBiosStatus == NRC_NOCALL && RetryCount < NETP_MAX_CALL_RETRY) { // // Initialize ASTAT NCB // RtlZeroMemory((PVOID) &Ncb, sizeof(NCB)); memcpy(Ncb.ncb_callname, CallNcb->ncb_callname, NCBNAMSZ); Ncb.ncb_buffer = (char FAR *) &StatusBuffer; Ncb.ncb_length = sizeof(StatusBuffer); Ncb.ncb_command = NCBASTAT; // Adapter status (wait) Ncb.ncb_lana_num = LanAdapterNumber; // // If failed, name is not present // if (Netbios(&Ncb) != NRC_GOODRET) { return NERR_NameNotFound; } Sleep(1000L); RetryCount++; NetBiosStatus = Netbios(CallNcb); } return NetpNetBiosStatusToApiStatus(NetBiosStatus); } int NetpSmbCheck( IN LPBYTE buffer, // Buffer containing SMB IN USHORT size, // size of SMB buffer (in bytes) IN UCHAR func, // Function code IN int parms, // Parameter count IN LPSTR fields // Buffer fields dope vector ) /*++ Routine Description: Checks Server Message Block for syntactical correctness This function is called to verify that a Server Message Block is of the specified form. The function returns zero if the SMB is correct; if an error is detected, a non-zero value indicating the nature of the error is returned. An SMB is a variable length structure whose exact size depends on the setting of certain fixed-offset fields and whose exact format cannot be determined except by examination of the whole structure. Smbcheck checks to see that an SMB conforms to a set of specified conditions. The "fields" parameter is a dope vector that describes the individual fields to be found in the buffer section at the end of the SMB. The vector is a null-terminated character string. Currently, the elements of the string must be as follows: 'b' - the next element in the buffer area should be a variable length buffer prefixed with a byte containing either 1 or 5 followed by two bytes containing the size of the buffer. 'd' - the next element in the buffer area is a null-terminated string prefixed with a byte containing 2. 'p' - the next element in the buffer area is a null-terminated string prefixed with a byte containing 3. 's' - the next element in the buffer area is a null-terminated string prefixed with a byte containing 4. Arguments: buffer - a pointer to the buffer containing the SMB size - the number of bytes in the buffer func - the expected SMB function code parms - the expected number of parameters fields - a dope vector describing the expected buffer fields within the SMB's buffer area (see below). Return Value: an integer status code; zero indicates no errors. --*/ { PSMB_HEADER smb; // SMB header pointer LPBYTE limit; // Upper limit smb = (PSMB_HEADER) buffer; // Overlay header with buffer // // Must be long enough for header // if(size < sizeof(SMB_HEADER)) { return(2); } // // Message type must be 0xFF // if(smb->Protocol[0] != 0xff) { return(3); } // // Server must be "SMB" // if( smb->Protocol[1] != 'S' || smb->Protocol[2] != 'M' || smb->Protocol[3] != 'B') { return(4); } // // Must have proper function code // if(smb->Command != func) { return(5); } limit = &buffer[size]; // Set upper limit of SMB buffer += sizeof(SMB_HEADER); // Skip over header // // Parameter counts must match // if(*buffer++ != (BYTE)parms) { return(6); } // // Skip parameters and buffer size // buffer += (((SHORT)parms & 0xFF) + 1)*sizeof(SHORT); // // Check for overflow // if(buffer > limit) { return(7); } // // Loop to check buffer fields // while(*fields) { // // Switch on dope vector character // switch(*fields++) { case 'b': // Variable length data block if(*buffer != '\001' && *buffer != '\005') { return(8); } // // Check for block code // ++buffer; // Skip over block code size = (USHORT)*buffer++ & (USHORT)0xFF; // Get low-byte size size += ((USHORT)*buffer++ & (USHORT)0xFF)<< 8; // Get high-byte of buffer size buffer += size; // Increment pointer break; case 'd': // Null-terminated dialect string if(*buffer++ != '\002') { // Check for string code return(9); } buffer += strlen((LPVOID) buffer) + 1; // Skip over the string break; case 'p': // Null-terminated path string if(*buffer++ != '\003') { // Check for string code return(10); } buffer += strlen((LPVOID) buffer) + 1; // Skip over the string break; case 's': // Null-terminated string if(*buffer++ != '\004') { // Check for string code return(11); } buffer += strlen((LPVOID) buffer) + 1; // Skip over the string break; } // // Check against end of block // if(buffer > limit) { return(12); } } return(buffer != limit); // Should be false }