/*++ Copyright (C) Microsoft Corporation, 1992 - 1999 Module Name: nsiclnt.cxx Abstract: This is the client side NSI service support layer. These are wrappers which call the name service provider. Author: Steven Zeck (stevez) 03/04/92 --*/ #include #include #include // This structure is used in binding handle select processing. typedef struct { unsigned long Count; int IndexMatch[1]; } MATCH_VECTOR; RPC_STATUS RPC_ENTRY I_NsBindingFoundBogus(RPC_BINDING_HANDLE *BindingHandle, DWORD BindId); RPC_STATUS RPC_ENTRY I_NsClientBindSearch(RPC_BINDING_HANDLE *NsiClntBinding, DWORD *BindId); void RPC_ENTRY I_NsClientBindDone(RPC_BINDING_HANDLE *NsiClntBinding, DWORD BindId); RPC_STATUS RPC_ENTRY RpcNsBindingLookupBeginW( IN unsigned long EntryNameSyntax, IN unsigned short __RPC_FAR * EntryName, IN RPC_IF_HANDLE RpcIfHandle, IN UUID __RPC_FAR * Object OPTIONAL, IN unsigned long BindingMaxCount, OUT RPC_NS_HANDLE *LookupContext ) /*++ Routine Description: Query the named server for the requested binding handles. This will request a query from name server to be performed and store the results to be retrieved with RpcNsBindingLookupNext(). Arguments: EntryNameSyntax - This value describes the type/format of the EntryName. EntryName - Name that this export will be stored in. This is just a token that is passed on the the Name Server. RpcIfHandle - The interface that is being exported. Object - the object UUID that you are looking for (or in combination with RpcIfHandle). BindingMaxCount - The maxium size of the binding vector to be returned to the RpcNsBindingLookupNext function. LookupContext - handle to be used to pass to RpcNsBindingImportNext, This is really allocated by RpcNsLookupBinding SearchOptions - used by the auto handle binding routines and Micosoft name server. Returns: RPC_S_OK, RPC_S_NO_MORE_BINDINGS --*/ { RPC_STATUS status; UNSIGNED16 NsiStatus; NSI_INTERFACE_ID_T NilIfOnWire, __RPC_FAR *IfPtr; RPC_BINDING_HANDLE NsiClntBinding = NULL; DWORD BindId = 0; if (RpcIfHandle == NULL) { IfPtr = &NilIfOnWire; memset(IfPtr, 0, sizeof(NSI_INTERFACE_ID_T)); } else { IfPtr = (NSI_INTERFACE_ID_T __RPC_FAR *) &((PRPC_CLIENT_INTERFACE)RpcIfHandle)->InterfaceId; } while ((status = I_NsClientBindSearch(&NsiClntBinding, &BindId)) == RPC_S_OK) { // Provide the default entry name if there is none. if ((! EntryName || *EntryName == 0) && DefaultName) { EntryName = &(*DefaultName); EntryNameSyntax = DefaultSyntax; } else if (! EntryNameSyntax) EntryNameSyntax = DefaultSyntax; RpcTryExcept { nsi_binding_lookup_begin(NsiClntBinding, EntryNameSyntax, EntryName, IfPtr, (NSI_UUID_P_T) Object, BindingMaxCount, 0, LookupContext, &NsiStatus); } RpcExcept(1) { *LookupContext = 0; NsiStatus = MapException(RpcExceptionCode()); } RpcEndExcept status = NsiMapStatus(NsiStatus); if (NsiStatus != NSI_S_NAME_SERVICE_UNAVAILABLE) break; I_NsBindingFoundBogus(&NsiClntBinding, BindId); } I_NsClientBindDone(&NsiClntBinding, BindId); return(status); } RPC_STATUS RPC_ENTRY RpcNsBindingLookupNext( IN RPC_NS_HANDLE LookupContext, OUT RPC_BINDING_VECTOR **BindingVector ) /*++ Routine Description: Retrieve the next group of bindings queryed from RpcNsBindingLookupBegin(). Arguments: LookupContext - handle to allocated by RpcNsBindingLookupBegin() BindingVector - returns a pointer to a binding vector. Returns: RPC_S_OK, RPC_S_NO_MORE_BINDINGS, RPC_S_OUT_OF_MEMORY, nsi_binding_lookup_next() --*/ { RPC_STATUS status; UNSIGNED16 NsiStatus; NSI_BINDING_VECTOR_T * NsiBindingVector; RPC_BINDING_HANDLE Handle; unsigned int HandleValide, Index; NsiBindingVector = 0; *BindingVector = 0; RpcTryExcept { nsi_binding_lookup_next((NSI_NS_HANDLE_T *) LookupContext, &NsiBindingVector, &NsiStatus); } RpcExcept(1) { NsiStatus = MapException(RpcExceptionCode()); } RpcEndExcept if (NsiStatus) return(NsiMapStatus(NsiStatus)); // Convert the string bindings to binding handles. This done by // replacing the StringBinding with RPC_BINDING_HANDLE to a // RPC_BINDING_VECTOR allocated by the runtime. *BindingVector = (RPC_BINDING_VECTOR *) I_RpcAllocate((unsigned int) ( sizeof(RPC_BINDING_VECTOR) - sizeof(RPC_BINDING_HANDLE) + sizeof(RPC_BINDING_HANDLE) * NsiBindingVector->count)); if (! *BindingVector) return(RPC_S_OUT_OF_MEMORY); for (Index = 0, HandleValide = 0; Index < NsiBindingVector->count; Index++) { Handle = 0; if (!UnicodeToRtString(NsiBindingVector->binding[Index].string)) status = RpcBindingFromStringBinding( (RT_CHAR *)NsiBindingVector->binding[Index].string, &Handle); if (!status && NsiBindingVector->binding[Index].entry_name) { if (!UnicodeToRtString( NsiBindingVector->binding[Index].entry_name)) { #ifdef NTENV status = I_RpcNsBindingSetEntryNameW(Handle, #else status = I_RpcNsBindingSetEntryName(Handle, #endif NsiBindingVector->binding[Index].entry_name_syntax, (RT_CHAR *)NsiBindingVector->binding[Index].entry_name); } } if (NsiBindingVector->binding[Index].entry_name) I_RpcFree(NsiBindingVector->binding[Index].entry_name); I_RpcFree(NsiBindingVector->binding[Index].string); // only copy the handle to the output if the Binding was OK. if (! status) (*BindingVector)->BindingH[HandleValide++] = Handle; } (*BindingVector)->Count = HandleValide; I_RpcFree(NsiBindingVector); return((HandleValide > 0)? RPC_S_OK: RPC_S_NO_MORE_BINDINGS); } RPC_STATUS RPC_ENTRY RpcNsBindingLookupDone( OUT RPC_NS_HANDLE *LookupContext ) /*++ Routine Description: Close the context opened with RpcNsBindingLookupBegin(); Arguments: LookupContext - context handle to close Returns: nsi_binding_lookup_done() --*/ { UNSIGNED16 NsiStatus = NSI_S_OK; RpcTryExcept { nsi_binding_lookup_done((NSI_NS_HANDLE_T *) LookupContext, &NsiStatus); } RpcExcept(1) { NsiStatus = MapException(RpcExceptionCode()); } RpcEndExcept // RpcBindingFree(&NsiClntBinding); *LookupContext = 0; return(NsiMapStatus(NsiStatus)); } RPC_STATUS RPC_ENTRY RpcNsBindingImportBeginW( IN unsigned long EntryNameSyntax, IN unsigned short __RPC_FAR * EntryName, IN RPC_IF_HANDLE RpcIfHandle, IN UUID __RPC_FAR * Object OPTIONAL, OUT RPC_NS_HANDLE *ImportContextOut ) /*++ Routine Description: Query the named server for the requested binding handles. This function is implemented in terms of RpcNsLookupBinding. Arguments: EntryNameSyntax - This value describes the type/format of the EntryName. EntryName - Name that this export will be stored in. This is just a token that is passed on the the Name Server. RpcIfHandle - The interface that is being exported. Object - the object UUID that you are looking for (or in combination with RpcIfHandle). ImportContext - handle to be used to pass to RpcNsBindingImportNext, This is really allocated by RpcNsLookupBinding SearchOptions - used by the auto handle binding routines and Micosoft name server. Returns: RpcNsBindingLookupBegin(), RPC_S_OUT_OF_MEMORY, RPS_S_OK --*/ { RPC_STATUS status; RPC_NS_HANDLE LookupContext; PRPC_IMPORT_CONTEXT_P ImportContext; const int BindingVectorSize = 10; *ImportContextOut = 0; status = RpcNsBindingLookupBeginW(EntryNameSyntax, EntryName, RpcIfHandle, Object, BindingVectorSize, &LookupContext); if (status) return(status); // Allocate an import context which contains a lookup context, // a StringBinding vector and an index to the current StringBinding // in the vector. if (!(ImportContext = (PRPC_IMPORT_CONTEXT_P) I_RpcAllocate(sizeof(RPC_IMPORT_CONTEXT_P))) ) return(RPC_S_OUT_OF_MEMORY); ImportContext->LookupContext = LookupContext; ImportContext->Bindings = 0; *ImportContextOut = ImportContext; return(RPC_S_OK); } RPC_STATUS RPC_ENTRY RpcNsBindingImportNext( IN RPC_NS_HANDLE ImportContextIn, OUT RPC_BINDING_HANDLE __RPC_FAR * RpcBinding ) /*++ Routine Description: Get the next StringBinding in the Import StringBinding vector. If the vector is empty, call RpcNsBindingLookupBegin() to get a new vector. Arguments: ImportContext - handle to be used get a new string binding vector from RpcNsBindingLookupNext() RpcBinding - place to return a binding. This Binding Handle ownership passes to caller. Returns: RPC_S_OK, RpcNsBindingLookupNext() --*/ { RPC_STATUS status; PRPC_IMPORT_CONTEXT_P ImportContext; ImportContext = (PRPC_IMPORT_CONTEXT_P) ImportContextIn; if (!ImportContext) return(RPC_S_NO_CONTEXT_AVAILABLE); if (ImportContext->Bindings) { status = RpcNsBindingSelect(ImportContext->Bindings, RpcBinding); if (status == RPC_S_OK) return(RPC_S_OK); if (status != RPC_S_NO_MORE_BINDINGS) return(status); } // The vector was empty or there were no more entris. Get another vector. if (ImportContext->Bindings) RpcBindingVectorFree(&ImportContext->Bindings); status = RpcNsBindingLookupNext(ImportContext->LookupContext, &ImportContext->Bindings); if (status) return(status); return(RpcNsBindingSelect(ImportContext->Bindings, RpcBinding)); } RPC_STATUS RPC_ENTRY RpcNsBindingImportDone( IN RPC_NS_HANDLE *ImportContextIn ) /*++ Routine Description: Close an Import Context handle when done. Free up the current Bindings vector, LookupContext and ImportContext structure. Arguments: ImportContext - handle to close. Returns: RPC_S_OK, RpcNsBindingLookupDone() --*/ { RPC_STATUS status; PRPC_IMPORT_CONTEXT_P ImportContext; ImportContext = (PRPC_IMPORT_CONTEXT_P) *ImportContextIn; if (! ImportContext) return(RPC_S_OK); if (ImportContext->Bindings) RpcBindingVectorFree(&ImportContext->Bindings); status = RpcNsBindingLookupDone(&ImportContext->LookupContext); I_RpcFree (ImportContext); *ImportContextIn = 0; return(status); } RPC_STATUS RPC_ENTRY RpcNsMgmtHandleSetExpAge( IN RPC_NS_HANDLE NsHandle, IN unsigned long ExpirationAge ) /*++ Routine Description: Set the maxium age that a cached entry will be returned in reponse to a name service inquirary transaction. Arguments: NsHandle - context handle created with one of the RpcNs*Begin APIs Returns: nsi_mgmt_handle_set_exp_age() --*/ { UNSIGNED16 NsiStatus; RPC_NS_HANDLE LookupContext = ((PRPC_IMPORT_CONTEXT_P)NsHandle)->LookupContext; RpcTryExcept { nsi_mgmt_handle_set_exp_age(LookupContext, ExpirationAge, &NsiStatus); } RpcExcept(1) { NsiStatus = MapException(RpcExceptionCode()); } RpcEndExcept return(NsiMapStatus(NsiStatus)); } #define isLocalName(NetWorkAddress) (1) //BUGBUG static RPC_STATUS GetMatchingProtocols( IN RPC_BINDING_VECTOR *BindingVector, OUT MATCH_VECTOR *MatchVector, IN char * SearchProtocol, OPTIONAL IN int fLocalOnly ) /*++ Routine Description: Construct a match binding vector with protocols that we are interested in. PERF: When we know how to parse NetWorkAddress to know when it is a local name, we should select those first Arguments: BindingVector - vector of binding handles to select from. MatchVector - place to put the results. SearchProtocol - Protocol we are looking for. A Nil matches everything. Returns: The number of protocols that we matched in the match vector. RPC_S_OK, RpcBindingToStringBinding(), RpcStringBindingParse() --*/ { RPC_STATUS Status; unsigned int Index; RT_CHAR * StringBinding, *ProtocolSeq, *NetAddress; int fProtocolsMatched; MatchVector->Count = 0; for (Index = 0; Index < BindingVector->Count; Index++) { if (!BindingVector->BindingH[Index]) continue; // Convert the binding handle to a string and then extract // the fields we are interested in. if (Status = RpcBindingToStringBinding( BindingVector->BindingH[Index], &StringBinding)) return (Status); if (Status = RpcStringBindingParse(StringBinding, 0, &ProtocolSeq, &NetAddress, 0, 0)) return (Status); fProtocolsMatched = 1; if (SearchProtocol) { char * STmp = SearchProtocol; for (RT_CHAR *pT = ProtocolSeq; *pT && (char) *pT++ == *STmp++; ) ; if (*STmp) fProtocolsMatched = 0; } // If we are looking for a local name only and the matched // protocol isn't local, throw this one out. if (fLocalOnly && !isLocalName(NetAddress)) fProtocolsMatched = 0; // Return all the strings to the RPC runtime. if (Status = RpcStringFree(&ProtocolSeq)) return(Status); if (Status = RpcStringFree(&NetAddress)) return(Status); if (Status = RpcStringFree(&StringBinding)) return(Status); if (! fProtocolsMatched) continue; // A match is recorded as an index into the original vector. MatchVector->IndexMatch[MatchVector->Count++] = Index; } return(RPC_S_OK); } int RandomNumber( ) /*++ Routine Description: Yet another pseudo-random number generator. Returns: New random number, in the range 0..32767. --*/ { static long holdrand; static int fInitialized = 0; // Start with a different seed everytime. if (!fInitialized) { fInitialized = 1; // holdrand = clock(); } return( (int) (holdrand = (long) ( (holdrand * 214013L + 2531011L) >> 16 & 0x7fff ) )); } RPC_STATUS RPC_ENTRY RpcNsBindingSelect( IN OUT RPC_BINDING_VECTOR *BindingVector, OUT RPC_BINDING_HANDLE __RPC_FAR * RpcBinding ) /*++ Routine Description: This function will select a Binding handle from a vector of binding handles. Since we know a little bit about our binding handles, we will chose the more effiecent types of handles first. We select groups of bindings unordered via a random number generator. Arguments: BindingVector - vector of binding handles to select from RpcBinding - place to return the binding handle. The ownership of the handle passes to the caller. Returns: RPC_S_OK, RPC_S_OUT_OF_MEMORY, RPC_S_NO_MORE_BINDINGS, GetMatchingProtocols() --*/ { static char * PreferredProtocol[] = { "ncalrpc", "ncacn_np", 0 }; int CountPreferredProtocol = sizeof(PreferredProtocol) / sizeof(void *); RPC_STATUS Status; MATCH_VECTOR *MatchVector; int IndexSelected; int ProtocolIndex; int fLocalOnly; *RpcBinding = 0; MatchVector = (MATCH_VECTOR *) I_RpcAllocate((unsigned int) (sizeof(MATCH_VECTOR) + sizeof(int) * BindingVector->Count)); if (!MatchVector) return(RPC_S_OUT_OF_MEMORY); // For all the protocols returned, first try the local ones, then // the remote. for (fLocalOnly = 1; fLocalOnly >= 0; fLocalOnly--) { for (ProtocolIndex = 0; ProtocolIndex < CountPreferredProtocol; ProtocolIndex++) { // First, get the perferred protocols into a match vector. // The match vector has a range from 0..number of matching protocols. // We need this so we know what range to generate a random number. if (Status = GetMatchingProtocols(BindingVector, MatchVector, PreferredProtocol[ProtocolIndex], fLocalOnly)) return(Status); // If we found any, select one and return it. if (MatchVector->Count) { IndexSelected = MatchVector-> IndexMatch[RandomNumber() % MatchVector->Count]; *RpcBinding = BindingVector->BindingH[IndexSelected]; // Remove selected one from binding vector. BindingVector->BindingH[IndexSelected] = 0; I_RpcFree (MatchVector); return(RPC_S_OK); } } } I_RpcFree (MatchVector); return(RPC_S_NO_MORE_BINDINGS); } RPC_STATUS RPC_ENTRY RpcNsBindingLookupBeginA( IN unsigned long EntryNameSyntax, IN unsigned char __RPC_FAR * EntryName, IN RPC_IF_HANDLE RpcIfSpec, IN UUID __RPC_FAR * ObjUuid OPTIONAL, IN unsigned long BindingMaxCount, OUT RPC_NS_HANDLE *LookupContext ) /*++ Routine Description: This is an ASCII wrapper to the UNICODE version of the API. It converts all char * -> short * strings and calls the UNICODE version. --*/ { WIDE_STRING EntryNameW(EntryName); if (EntryNameW.OutOfMemory()) return(RPC_S_OUT_OF_MEMORY); return(RpcNsBindingLookupBeginW(EntryNameSyntax, &EntryNameW, RpcIfSpec, ObjUuid, BindingMaxCount, LookupContext)); } RPC_STATUS RPC_ENTRY RpcNsBindingImportBeginA( IN unsigned long EntryNameSyntax, IN unsigned char __RPC_FAR * EntryName, IN RPC_IF_HANDLE RpcIfSpec, IN UUID __RPC_FAR * ObjUuid OPTIONAL, OUT RPC_NS_HANDLE *ImportContext ) /*++ Routine Description: This is an ASCII wrapper to the UNICODE version of the API. It converts all char * -> short * strings and calls the UNICODE version. --*/ { WIDE_STRING EntryNameW(EntryName); if (EntryNameW.OutOfMemory()) return(RPC_S_OUT_OF_MEMORY); return(RpcNsBindingImportBeginW(EntryNameSyntax, &EntryNameW, RpcIfSpec, ObjUuid, ImportContext)); }