/* * NDR data marshalling * * Copyright 2006 Mike McCormack (for CodeWeavers) * Copyright 2006-2007 Robert Shearman (for CodeWeavers) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "ndr_misc.h" #include "rpc_assoc.h" #include "rpcndr.h" #include "wine/rpcfc.h" #include "wine/debug.h" #include "wine/list.h" WINE_DEFAULT_DEBUG_CHANNEL(ole); #define NDR_CONTEXT_HANDLE_MAGIC 0x4352444e typedef struct ndr_context_handle { ULONG attributes; GUID uuid; } ndr_context_handle; struct context_handle_entry { struct list entry; DWORD magic; RPC_BINDING_HANDLE handle; ndr_context_handle wire_data; }; static struct list context_handle_list = LIST_INIT(context_handle_list); static CRITICAL_SECTION ndr_context_cs; static CRITICAL_SECTION_DEBUG ndr_context_debug = { 0, 0, &ndr_context_cs, { &ndr_context_debug.ProcessLocksList, &ndr_context_debug.ProcessLocksList }, 0, 0, { (DWORD_PTR)(__FILE__ ": ndr_context") } }; static CRITICAL_SECTION ndr_context_cs = { &ndr_context_debug, -1, 0, 0, 0, 0 }; static struct context_handle_entry *get_context_entry(NDR_CCONTEXT CContext) { struct context_handle_entry *che = CContext; if (che->magic != NDR_CONTEXT_HANDLE_MAGIC) return NULL; return che; } static struct context_handle_entry *context_entry_from_guid(LPCGUID uuid) { struct context_handle_entry *che; LIST_FOR_EACH_ENTRY(che, &context_handle_list, struct context_handle_entry, entry) if (IsEqualGUID(&che->wire_data.uuid, uuid)) return che; return NULL; } RPC_BINDING_HANDLE WINAPI NDRCContextBinding(NDR_CCONTEXT CContext) { struct context_handle_entry *che; RPC_BINDING_HANDLE handle = NULL; TRACE("%p\n", CContext); EnterCriticalSection(&ndr_context_cs); che = get_context_entry(CContext); if (che) handle = che->handle; LeaveCriticalSection(&ndr_context_cs); if (!handle) { ERR("invalid handle %p\n", CContext); RpcRaiseException(RPC_X_SS_CONTEXT_MISMATCH); } return handle; } void WINAPI NDRCContextMarshall(NDR_CCONTEXT CContext, void *pBuff) { struct context_handle_entry *che; TRACE("%p %p\n", CContext, pBuff); if (CContext) { EnterCriticalSection(&ndr_context_cs); che = get_context_entry(CContext); memcpy(pBuff, &che->wire_data, sizeof (ndr_context_handle)); LeaveCriticalSection(&ndr_context_cs); } else { ndr_context_handle *wire_data = pBuff; wire_data->attributes = 0; wire_data->uuid = GUID_NULL; } } /*********************************************************************** * RpcSmDestroyClientContext [RPCRT4.@] */ RPC_STATUS WINAPI RpcSmDestroyClientContext(void **ContextHandle) { RPC_STATUS status = RPC_X_SS_CONTEXT_MISMATCH; struct context_handle_entry *che = NULL; TRACE("(%p)\n", ContextHandle); EnterCriticalSection(&ndr_context_cs); che = get_context_entry(*ContextHandle); *ContextHandle = NULL; if (che) { status = RPC_S_OK; list_remove(&che->entry); } LeaveCriticalSection(&ndr_context_cs); if (che) { RpcBindingFree(&che->handle); HeapFree(GetProcessHeap(), 0, che); } return status; } /*********************************************************************** * RpcSsDestroyClientContext [RPCRT4.@] */ void WINAPI RpcSsDestroyClientContext(void **ContextHandle) { RPC_STATUS status = RpcSmDestroyClientContext(ContextHandle); if (status != RPC_S_OK) RpcRaiseException(status); } /*********************************************************************** * RpcSsDontSerializeContext [RPCRT4.@] */ void WINAPI RpcSsDontSerializeContext(void) { FIXME("stub\n"); } static RPC_STATUS ndr_update_context_handle(NDR_CCONTEXT *CContext, RPC_BINDING_HANDLE hBinding, const ndr_context_handle *chi) { struct context_handle_entry *che = NULL; /* a null UUID means we should free the context handle */ if (IsEqualGUID(&chi->uuid, &GUID_NULL)) { if (*CContext) { che = get_context_entry(*CContext); if (!che) return RPC_X_SS_CONTEXT_MISMATCH; list_remove(&che->entry); RpcBindingFree(&che->handle); HeapFree(GetProcessHeap(), 0, che); che = NULL; } } /* if there's no existing entry matching the GUID, allocate one */ else if (!(che = context_entry_from_guid(&chi->uuid))) { che = HeapAlloc(GetProcessHeap(), 0, sizeof *che); if (!che) return RPC_X_NO_MEMORY; che->magic = NDR_CONTEXT_HANDLE_MAGIC; RpcBindingCopy(hBinding, &che->handle); list_add_tail(&context_handle_list, &che->entry); che->wire_data = *chi; } *CContext = che; return RPC_S_OK; } /*********************************************************************** * NDRCContextUnmarshall [RPCRT4.@] */ void WINAPI NDRCContextUnmarshall(NDR_CCONTEXT *CContext, RPC_BINDING_HANDLE hBinding, void *pBuff, ULONG DataRepresentation) { RPC_STATUS status; TRACE("*%p=(%p) %p %p %08x\n", CContext, *CContext, hBinding, pBuff, DataRepresentation); EnterCriticalSection(&ndr_context_cs); status = ndr_update_context_handle(CContext, hBinding, pBuff); LeaveCriticalSection(&ndr_context_cs); if (status) RpcRaiseException(status); } /*********************************************************************** * NDRSContextMarshall [RPCRT4.@] */ void WINAPI NDRSContextMarshall(NDR_SCONTEXT SContext, void *pBuff, NDR_RUNDOWN userRunDownIn) { TRACE("(%p %p %p)\n", SContext, pBuff, userRunDownIn); NDRSContextMarshall2(I_RpcGetCurrentCallHandle(), SContext, pBuff, userRunDownIn, NULL, RPC_CONTEXT_HANDLE_DEFAULT_FLAGS); } /*********************************************************************** * NDRSContextMarshallEx [RPCRT4.@] */ void WINAPI NDRSContextMarshallEx(RPC_BINDING_HANDLE hBinding, NDR_SCONTEXT SContext, void *pBuff, NDR_RUNDOWN userRunDownIn) { TRACE("(%p %p %p %p)\n", hBinding, SContext, pBuff, userRunDownIn); NDRSContextMarshall2(hBinding, SContext, pBuff, userRunDownIn, NULL, RPC_CONTEXT_HANDLE_DEFAULT_FLAGS); } /*********************************************************************** * NDRSContextMarshall2 [RPCRT4.@] */ void WINAPI NDRSContextMarshall2(RPC_BINDING_HANDLE hBinding, NDR_SCONTEXT SContext, void *pBuff, NDR_RUNDOWN userRunDownIn, void *CtxGuard, ULONG Flags) { RpcBinding *binding = hBinding; RPC_STATUS status; ndr_context_handle *ndr = pBuff; TRACE("(%p %p %p %p %p %u)\n", hBinding, SContext, pBuff, userRunDownIn, CtxGuard, Flags); if (!binding->server || !binding->Assoc) RpcRaiseException(RPC_S_INVALID_BINDING); if (Flags & RPC_CONTEXT_HANDLE_FLAGS) FIXME("unimplemented flags: 0x%x\n", Flags & RPC_CONTEXT_HANDLE_FLAGS); if (SContext->userContext) { status = RpcServerAssoc_UpdateContextHandle(binding->Assoc, SContext, CtxGuard, userRunDownIn); if (status != RPC_S_OK) RpcRaiseException(status); ndr->attributes = 0; RpcContextHandle_GetUuid(SContext, &ndr->uuid); RPCRT4_RemoveThreadContextHandle(SContext); RpcServerAssoc_ReleaseContextHandle(binding->Assoc, SContext, TRUE); } else { if (!RpcContextHandle_IsGuardCorrect(SContext, CtxGuard)) RpcRaiseException(RPC_X_SS_CONTEXT_MISMATCH); memset(ndr, 0, sizeof(*ndr)); RPCRT4_RemoveThreadContextHandle(SContext); /* Note: release the context handle twice in this case to release * one ref being kept around for the data and one ref for the * unmarshall/marshall sequence */ if (!RpcServerAssoc_ReleaseContextHandle(binding->Assoc, SContext, TRUE)) return; /* this is to cope with the case of the data not being valid * before and so not having a further reference */ RpcServerAssoc_ReleaseContextHandle(binding->Assoc, SContext, FALSE); } } /*********************************************************************** * NDRSContextUnmarshall [RPCRT4.@] */ NDR_SCONTEXT WINAPI NDRSContextUnmarshall(void *pBuff, ULONG DataRepresentation) { TRACE("(%p %08x)\n", pBuff, DataRepresentation); return NDRSContextUnmarshall2(I_RpcGetCurrentCallHandle(), pBuff, DataRepresentation, NULL, RPC_CONTEXT_HANDLE_DEFAULT_FLAGS); } /*********************************************************************** * NDRSContextUnmarshallEx [RPCRT4.@] */ NDR_SCONTEXT WINAPI NDRSContextUnmarshallEx(RPC_BINDING_HANDLE hBinding, void *pBuff, ULONG DataRepresentation) { TRACE("(%p %p %08x)\n", hBinding, pBuff, DataRepresentation); return NDRSContextUnmarshall2(hBinding, pBuff, DataRepresentation, NULL, RPC_CONTEXT_HANDLE_DEFAULT_FLAGS); } /*********************************************************************** * NDRSContextUnmarshall2 [RPCRT4.@] */ NDR_SCONTEXT WINAPI NDRSContextUnmarshall2(RPC_BINDING_HANDLE hBinding, void *pBuff, ULONG DataRepresentation, void *CtxGuard, ULONG Flags) { RpcBinding *binding = hBinding; NDR_SCONTEXT SContext; RPC_STATUS status; const ndr_context_handle *context_ndr = pBuff; TRACE("(%p %p %08x %p %u)\n", hBinding, pBuff, DataRepresentation, CtxGuard, Flags); if (!binding->server || !binding->Assoc) RpcRaiseException(RPC_S_INVALID_BINDING); if (Flags & RPC_CONTEXT_HANDLE_FLAGS) FIXME("unimplemented flags: 0x%x\n", Flags & RPC_CONTEXT_HANDLE_FLAGS); if (!pBuff || (!context_ndr->attributes && UuidIsNil((UUID *)&context_ndr->uuid, &status))) status = RpcServerAssoc_AllocateContextHandle(binding->Assoc, CtxGuard, &SContext); else { if (context_ndr->attributes) { ERR("non-null attributes 0x%x\n", context_ndr->attributes); status = RPC_X_SS_CONTEXT_MISMATCH; } else status = RpcServerAssoc_FindContextHandle(binding->Assoc, &context_ndr->uuid, CtxGuard, Flags, &SContext); } if (status != RPC_S_OK) RpcRaiseException(status); RPCRT4_PushThreadContextHandle(SContext); return SContext; }