/*++ Copyright (c) 1993 Microsoft Corporation Module Name: netbcom.c Abstract: NetBios transport common code between server and client. Author: Steven Zeck (stevez) 2/12/92 Danny Glasser (dannygl) 3/1/93 --*/ #include "NetBCom.h" // The maximum value of a lana number #define MAX_LANA_NUMBER UCHAR_MAX // This the netbios name of this (self) machine. // NOTE: We assume one-byte characters here unsigned char MachineName[NCBNAMSZ] = { 0 }; size_t MachineNameLengthUnpadded =0; #ifdef NTENV #define MAX_MAP_ENTRIES 20 void InitialNtRegistry( ) /*++ Routine Description: This function maps loads the RPC lana mappings from the NT registry. This information is automatically generated by NT setup and the Networks Control Panel applet. Because it can change dynamically, we load it all at once to achieve consistent data. --*/ { HKEY RegHandle; LONG status; int i; char protseq[64]; DWORD protseq_len; DWORD lana; DWORD lana_len; DWORD data_type; // This function gets called by both the client and the server. We // serialize access to make sure that the table gets initialized only // once. CRITICAL_ENTER(); // Return immediately if the table is already initialized. if (ProtocolTable[0].ProtoSeq) { CRITICAL_LEAVE(); return; } // Open the registry key for RPC NetBIOS information status = RegOpenKeyExA(RPC_REG_ROOT, REG_NETBIOS, 0, KEY_READ, &RegHandle); ASSERT(!status); if (status) { CRITICAL_LEAVE(); return; } // Enumerate the values for this key and load them into the protocol // table. for (i = 0; !status && i < MAX_LANA; i++) { protseq_len = sizeof(protseq); lana_len = sizeof(lana); status = RegEnumValueA(RegHandle, i, protseq, &protseq_len, NULL, &data_type, (LPBYTE) &lana, &lana_len); if (!status && data_type == REG_DWORD && lana <= MAX_LANA_NUMBER) { ProtocolTable[i].ProtoSeq = I_RpcAllocate(protseq_len + 1); ASSERT(ProtocolTable[i].ProtoSeq); if (! ProtocolTable[i].ProtoSeq) { status = RPC_S_OUT_OF_RESOURCES; } else { // Everything succeeded; store the data in the table. strcpy(ProtocolTable[i].ProtoSeq, protseq); ProtocolTable[i].Lana = (unsigned char) lana; } } else { // If any of the above conditions aren't met, then we should be // at the end of the list. ASSERT(status == ERROR_NO_MORE_ITEMS); } } RegCloseKey(RegHandle); CRITICAL_LEAVE(); return; } #endif // NTENV RPC_STATUS MapProtocol( IN RPC_CHAR *ProtoSeq, IN int DriverNumber, OUT PPROTOCOL_MAP *ProtocolEntry ) /*++ Routine Description: This function maps a protocol string into a protocol map entry. In the non-NT versions, it looks up the information dynamically in the registry. In the NT version, the registry information is pre-loaded Arguments: ProtoSeq - the protocol sequence that we want to map DriverNumber - the logical driver number for the protocol. ProtocolEntry - pointer to place to return the results. Return Value: RPC_S_OK, RPC_S_OUT_OF_RESOURCES, RPC_S_INVALID_ENDPOINT_FORMAT The output pointer is set to the corresponding entry when found. --*/ { long status; int i; HKEY RegHandle; char Protocol[40]; char LanaString[10]; long BufferLength = sizeof(LanaString); // Copy the possible unicode protocol string to ascii. for (i = 0; (Protocol[i] = (char) ProtoSeq[i]) && i < sizeof(Protocol); i++) ; // Add the logical driver number to the protocol string. This // allows multiple drivers (net cards) to be attached to the same // logical protocol. Protocol[i] = (char) ('0' + DriverNumber); Protocol[i+1] = 0; // First look in the proto sequences that we have already mapped. for (i = 0; ProtocolTable[i].ProtoSeq && i < MAX_LANA; i++) { // If found, set the output pointer. if (strcmp(ProtocolTable[i].ProtoSeq, Protocol) == 0) { *ProtocolEntry = &ProtocolTable[i]; return(RPC_S_OK); } } if (i >= MAX_LANA) return(RPC_S_OUT_OF_RESOURCES); // The name isn't found. In the NT version, this is an error; in the // non-NT versions, open the registry and see if a mapping is defined. #ifdef NTENV return(RPC_S_PROTSEQ_NOT_FOUND); #else status = RegOpenKey(RPC_REG_ROOT, REG_NETBIOS, &RegHandle); if (status) return(RPC_S_PROTSEQ_NOT_FOUND); status = RegQueryValue(RegHandle, Protocol, LanaString, &BufferLength); RegCloseKey(RegHandle); if (status || ! (*LanaString >= '0' && *LanaString <= '9')) return(RPC_S_PROTSEQ_NOT_FOUND); // Now we have a Lana number for the protocol sequence. Put this // info in the protocol to lana mapping structure. if (! (ProtocolTable[i].ProtoSeq = (char *) I_RpcAllocate(strlen(Protocol)+1)) ) { return(RPC_S_OUT_OF_RESOURCES); } strcpy(ProtocolTable[i].ProtoSeq, Protocol); ProtocolTable[i].Lana = (unsigned char) (*LanaString - '0'); *ProtocolEntry = &ProtocolTable[i]; return(RPC_S_OK); #endif // NTENV } RPC_STATUS RPC_ENTRY MapErrorCode ( IN ERROR_TABLE * MapTable, IN RPC_OS_ERROR Status, IN RPC_STATUS DefaultStatus ) /*++ Routine Description: This function maps a OS specific error code into a generic RPC status code. The ERROR_TABLE is an unordered list of pairs, with a value of 0 terminating the table. So don't try to translate a 0 OS_ERROR. You will see this routine called , with the return value ignored. This is done when we don't need the translated error code, but want to check for unexpected failures. Arguments: MapTable - The table to OS codes to RPC_STATUS codes. Status - The OS specific error that we wish to map. DefaultStatus - The status to return if none of the codes match. We will ASSERT in the debug version if we have to take this action. Return Value: The translated error code if there is match, else the value of DefaultStatus. --*/ { int TableIndex; ASSERT(MapTable); for (TableIndex = 0; MapTable[TableIndex].OScode != 0; TableIndex++) if (MapTable[TableIndex].OScode == Status) return (MapTable[TableIndex].RpcStatus); return (DefaultStatus); } int SetupNetBios ( IN RPC_CHAR * RpcProtocolSequence ) /*++ Routine Description: Loadable transport initialization function. Get the machine name of this workstation into MachineName and perform other OS specific initiailization. Arguments: RpcProtocolSequence - the protocol string that mapped to this library. Returns: TRUE if the initialization was OK. --*/ { #ifdef NTENV BOOLEAN BooleanStatus; DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1; unsigned char ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; BooleanStatus = GetComputerNameA(ComputerName, &ComputerNameLength); ASSERT( BooleanStatus == TRUE ); // Store padded machine name (and save unpadded length) MachineNameLengthUnpadded = strlen(ComputerName); memcpy(MachineName, ComputerName, MachineNameLengthUnpadded); memset(MachineName + MachineNameLengthUnpadded, NETBIOS_NAME_PAD_BYTE, sizeof(MachineName) - MachineNameLengthUnpadded); ASSERT(MachineNameLengthUnpadded < sizeof(MachineName)); InitialNtRegistry(); #else // DOS || WIN // We use the GetMachineName int21 because the Lanman APIs take up // 20K in the library (and are less portable). int failed = 0; _asm { push ds ; save DS before using it for call mov dx, offset MachineName mov ax, seg MachineName mov ds,ax mov ax, 05e00h ; call DOS to get machine name int 21h pop ds ; restore DS jc NameError ; carry flag set indicates error cmp ch, 0 ; ch == 0 indicates no net jne NameOK NameError: mov failed, 1 NameOK: }; if (failed) return 0; // BUGBUG - Set MachineNameLengthUnpadded here (if building a DOS // or Win16 server) #if !defined(WIN) // Since DOS .DLLs are imitations, ask the C runtime to call us // on process termination. I_DosAtExit(CleanUpNetBios); #else // Place an address in the per DLL termination table, see ltstart.asm DllTermination = CleanUpNetBios; #endif // WIN #endif // NTENV PUNUSED(RpcProtocolSequence); return(1); } #ifdef NTENV UCHAR RPC_ENTRY AdapterReset ( IN PPROTOCOL_MAP ProtocolEntry ) /*++ Routine Description: This function is used by both the client and server NetBIOS transports to submit the RESET NCB for a process, as is required by NetBIOS on NT. We need to perform this in a single place to allow a process to act as both an RPC client and server over NetBIOS. Otherwise, both the client and server transports would submit RESETs and the second one would destroy the state of the first. Arguments: ProtocolEntry - This is the entry in the protocol map for the adapter to be reset. Returns: The return code of the RESET NCB, if it's submitted. 0 if the RESET has already occurred. --*/ { unsigned char status = 0; // Make sure that no other threads are accessing this function CRITICAL_ENTER(); // Perform the reset, if necessary if (! ProtocolEntry->ResetDone) { NCB theNCB; memset(&theNCB, 0 , sizeof(theNCB)); theNCB.ncb_lana_num = ProtocolEntry->Lana; // The values are needed for the server theNCB.ncb_callname[0] = 254; // max sessions theNCB.ncb_callname[1] = 64; // max commands theNCB.ncb_callname[2] = 32; // max names status = execNCB(NCBRESET, &theNCB); // If the NCB was successfully invoked, get the status from the // completed NCB itself. if (status == 0) { status = theNCB.ncb_retcode; } // Set the flag so the reset is performed only once (per process). if (status == 0) { ProtocolEntry->ResetDone = 1; } } // Allow other threads to run again CRITICAL_LEAVE(); return status; } #endif //NTENV unsigned char RPC_ENTRY execNCB( IN unsigned char command, IN OUT NCB *pNCB ) /*++ Routine Description: Pass an NCB to the NetBios software to execute. This function hides the differences of the OS from the other parts of the driver. Arguments: command - command to execute pNCB - NCB to act on Returns: The result of ncb_retcode field from the NCB executed. --*/ { unsigned char result; pNCB->ncb_command = command; #if defined(WIN32RPC) result = Netbios(pNCB); #elif defined(WIN) // If the command is an async one, call the transport helper // functions to corrdinate the waiting. _asm { les bx,pNCB call NetBiosCall mov al,es:[bx+1] mov result,al } if (result == NRC_PENDING) result = 0; #else // DOS _asm { les bx, pNCB int 05ch ; call NetBios Directly mov result,al }; if (result == NRC_PENDING) result = 0; #endif return(result); }