summaryrefslogtreecommitdiff
path: root/nfc/Simulator/Src/FileContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'nfc/Simulator/Src/FileContext.cpp')
-rw-r--r--nfc/Simulator/Src/FileContext.cpp640
1 files changed, 640 insertions, 0 deletions
diff --git a/nfc/Simulator/Src/FileContext.cpp b/nfc/Simulator/Src/FileContext.cpp
new file mode 100644
index 00000000..6e609832
--- /dev/null
+++ b/nfc/Simulator/Src/FileContext.cpp
@@ -0,0 +1,640 @@
+/*++
+
+Copyright (C) Microsoft Corporation, All Rights Reserved
+
+Module Name:
+
+ filecontext.cpp
+
+Abstract:
+
+ This file implements the class for context associated with the file object
+
+Environment:
+
+ Windows User-Mode Driver Framework (WUDF)
+
+--*/
+
+#include "Internal.h"
+#include "FileContext.tmh"
+
+CFileObject::CFileObject(
+ WDFFILEOBJECT FileObject
+ )
+ : m_FileObject(FileObject),
+ m_Role(ROLE_UNDEFINED),
+ m_pszType(nullptr),
+ m_fEnabled(TRUE),
+ m_dwQueueSize(0),
+ m_cCompleteReady(0),
+ m_pConnection(nullptr),
+ m_Request(nullptr),
+ m_SecureElementEventType(ExternalReaderArrival)
+{
+ NT_ASSERT(m_FileObject != nullptr);
+
+ InitializeListHead(&m_Queue);
+ InitializeListHead(&m_ListEntry);
+ InitializeCriticalSection(&m_RoleLock);
+
+ RtlZeroMemory(&m_SecureElementId, sizeof(GUID));
+}
+
+CFileObject::~CFileObject()
+{
+ //
+ // The object is bound to the file handle and so this happens after close and the
+ // framework guarentees all requests on the file handle are cancelled prior to close
+ //
+ EnterCriticalSection(&m_RoleLock);
+ NT_ASSERT(m_Request == nullptr);
+ LeaveCriticalSection(&m_RoleLock);
+
+ PurgeQueue();
+
+ SAFE_DELETEARRAY(m_pszType);
+ SAFE_DELETE(m_pConnection);
+
+ DeleteCriticalSection(&m_RoleLock);
+
+ m_FileObject = nullptr;
+}
+
+VOID CFileObject::OnDestroy(_In_ WDFOBJECT FileObject)
+{
+ FunctionEntry("...");
+
+ CFileObject *pFileObject = GetFileObject(FileObject);
+
+ NT_ASSERT(pFileObject != nullptr);
+
+ if (pFileObject->m_FileObject == FileObject) {
+ // File object constructed using placement 'new' so explicitly invoke destructor
+ pFileObject->~CFileObject();
+ }
+
+ FunctionReturnVoid();
+}
+
+NTSTATUS CFileObject::Enable()
+{
+ MethodEntry("void");
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsPublication() || IsSubscription());
+
+ if (m_fEnabled) {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ goto Exit;
+ }
+
+ m_fEnabled = TRUE;
+
+Exit:
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturn(Status, "Status = %!STATUS!", Status);
+}
+
+NTSTATUS CFileObject::Disable()
+{
+ MethodEntry("void");
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsPublication() || IsSubscription());
+
+ if (!m_fEnabled) {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ goto Exit;
+ }
+
+ m_fEnabled = FALSE;
+ CompleteRequest(STATUS_CANCELLED, 0, true);
+ PurgeQueue();
+
+Exit:
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturn(Status, "Status = %!STATUS!", Status);
+}
+
+NTSTATUS CFileObject::SetType(_In_ PCWSTR pszType)
+{
+ MethodEntry("...");
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ size_t cchType = wcslen(pszType) + 1;
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(m_pszType == nullptr);
+
+ NT_ASSERT(IsPublication() || IsSubscription());
+
+ if ((cchType > MinCchType) && (cchType < MaxCchType)) {
+ m_pszType = new WCHAR[cchType];
+
+ if (m_pszType != nullptr) {
+ StringCchCopy(m_pszType, cchType, pszType);
+ }
+ else {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+ else {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturn(Status, "Status = %!STATUS!", Status);
+}
+
+NTSTATUS CFileObject::GetNextSubscribedMessage(_In_ WDFREQUEST Request)
+{
+ MethodEntry("...");
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ CPayload* pPayload = nullptr;
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsSubscription());
+
+ if (m_Request != nullptr) {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ goto Exit;
+ }
+
+ if (!m_fEnabled) {
+ Status = STATUS_CANCELLED;
+ goto Exit;
+ }
+
+ Status = WdfRequestMarkCancelableEx(Request, CFileObject::OnRequestCancel);
+
+ if (NT_SUCCESS(Status)) {
+ m_Request = Request;
+
+ if (!IsListEmpty(&m_Queue)) {
+ pPayload = CPayload::FromListEntry(m_Queue.Flink);
+
+ if (CompleteRequest(pPayload->GetSize(), pPayload->GetPayload())) {
+ m_dwQueueSize--;
+ RemoveHeadList(&m_Queue);
+ delete pPayload;
+ }
+ }
+ }
+
+Exit:
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturn(Status, "Status = %!STATUS!", Status);
+}
+
+NTSTATUS CFileObject::SetPayload(_In_ DWORD cbPayload, _In_reads_bytes_(cbPayload) PBYTE pbPayload)
+{
+ MethodEntry("...");
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsPublication());
+
+ if (m_Payload.GetPayload() != nullptr) {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ goto Exit;
+ }
+
+ Status = m_Payload.Initialize(cbPayload, pbPayload);
+
+Exit:
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturn(Status, "Status = %!STATUS!", Status);
+}
+
+NTSTATUS CFileObject::GetNextTransmittedMessage(_In_ WDFREQUEST Request)
+{
+ MethodEntry("...");
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsPublication());
+
+ if (m_Request != nullptr) {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ goto Exit;
+ }
+
+ if (!m_fEnabled) {
+ Status = STATUS_CANCELLED;
+ goto Exit;
+ }
+
+ Status = WdfRequestMarkCancelableEx(Request, CFileObject::OnRequestCancel);
+
+ if (NT_SUCCESS(Status)) {
+ m_Request = Request;
+
+ if (m_cCompleteReady > 0) {
+ if (CompleteRequest(STATUS_SUCCESS, 0, true)) {
+ m_cCompleteReady--;
+ }
+ }
+ }
+
+Exit:
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturn(Status, "Status = %!STATUS!", Status);
+}
+
+NTSTATUS CFileObject::GetNextSecureElementPayload(_In_ WDFREQUEST Request)
+{
+ MethodEntry("...");
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ CPayload* pPayload = nullptr;
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsSecureElementEvent() || IsSecureElementManager());
+
+ if (m_Request != nullptr) {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ goto Exit;
+ }
+
+ Status = WdfRequestMarkCancelableEx(Request, CFileObject::OnRequestCancel);
+
+ if (NT_SUCCESS(Status)) {
+ m_Request = Request;
+
+ if (!IsListEmpty(&m_Queue)) {
+ pPayload = CPayload::FromListEntry(m_Queue.Flink);
+
+ if (CompleteRequest(pPayload->GetSize(), pPayload->GetPayload())) {
+ m_dwQueueSize--;
+ RemoveHeadList(&m_Queue);
+ delete pPayload;
+ }
+ }
+ }
+
+Exit:
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturn(Status, "Status = %!STATUS!", Status);
+}
+
+NTSTATUS CFileObject::SubscribeForEvent(_In_ GUID& SecureElementId, _In_ SECURE_ELEMENT_EVENT_TYPE SecureElementEventType)
+{
+ MethodEntry("...");
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsSecureElementEvent());
+
+ m_SecureElementId = SecureElementId;
+ m_SecureElementEventType = SecureElementEventType;
+
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturn(Status, "Status = %!STATUS!", Status);
+}
+
+NTSTATUS
+CFileObject::BeginProximity(
+ _In_ BEGIN_PROXIMITY_ARGS *pArgs,
+ _In_ IConnectionCallback* pCallback
+ )
+{
+ MethodEntry("...");
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ NT_ASSERT(IsRoleSimulation());
+
+ if (CConnection::Create(pCallback, &m_pConnection)) {
+ if (SUCCEEDED(m_pConnection->InitializeAsClient(pArgs))) {
+ Status = STATUS_SUCCESS;
+ }
+ else {
+ Status = STATUS_INTERNAL_ERROR;
+ }
+ }
+ else {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ MethodReturn(Status, "Status = %!STATUS!", Status);
+}
+
+void CFileObject::HandleArrivalEvent()
+{
+ MethodEntry("void");
+
+ DWORD dwFlags = 1; // payload for arrival event
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsArrivedSubscription());
+
+ if (m_fEnabled) {
+ HandleReceivedMessage(sizeof(DWORD), (PBYTE) &dwFlags);
+ }
+
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturnVoid();
+}
+
+void CFileObject::HandleRemovalEvent()
+{
+ MethodEntry("void");
+
+ DWORD dwFlags = 0; // payload for removal event
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsDepartedSubscription());
+
+ if (m_fEnabled) {
+ HandleReceivedMessage(sizeof(DWORD), (PBYTE) &dwFlags);
+ }
+
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturnVoid();
+}
+
+void CFileObject::HandleMessageTransmitted(void)
+{
+ MethodEntry("m_fEnabled=%!bool!", m_fEnabled);
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsPublication());
+
+ if (m_fEnabled) {
+ if (!CompleteRequest(STATUS_SUCCESS, 0, true)) {
+ m_cCompleteReady++;
+ }
+ }
+
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturnVoid();
+}
+
+void
+CFileObject::HandleReceivedMessage(
+ _In_ PCWSTR pszType,
+ _In_ DWORD cbPayload,
+ _In_reads_bytes_(cbPayload) PBYTE pbPayload
+ )
+{
+ MethodEntry("Enabled=%!bool!", m_fEnabled);
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsNormalSubscription());
+
+ if (m_fEnabled) {
+ if ((CompareStringOrdinal(m_pszType, -1, WINDOWSMIME_PROTOCOL, -1, FALSE) == CSTR_EQUAL) &&
+ (wcslen(pszType) > WINDOWSMIME_PROTOCOL_CHARS) &&
+ (CompareStringOrdinal(pszType, WINDOWSMIME_PROTOCOL_CHARS, WINDOWSMIME_PROTOCOL, -1, FALSE) == CSTR_EQUAL)) {
+
+ CHAR szMimeType[MaxCchMimeType + 1] = {};
+ BYTE* pbNewPayload = new BYTE[cbPayload + MaxCchMimeType];
+
+ if (pbNewPayload != nullptr) {
+ if (SUCCEEDED(StringCchPrintfA(szMimeType, _countof(szMimeType), "%S", pszType + WINDOWSMIME_PROTOCOL_CHARS + 1))) {
+ RtlCopyMemory(pbNewPayload, szMimeType, MaxCchMimeType);
+ RtlCopyMemory(pbNewPayload + MaxCchMimeType, pbPayload, cbPayload);
+
+ HandleReceivedMessage(cbPayload + MaxCchMimeType, pbNewPayload);
+ }
+
+ SAFE_DELETEARRAY(pbNewPayload);
+ }
+ }
+ else if (CompareStringOrdinal(pszType, -1, m_pszType, -1, FALSE) == CSTR_EQUAL) {
+ if (CompareStringOrdinal(m_pszType, -1, WINDOWSURI_PROTOCOL, -1, FALSE) == CSTR_EQUAL) {
+ // WindowsUri must be returned as NULL terminated UTF16
+ DWORD cbNewPayload = cbPayload + sizeof(WCHAR);
+ BYTE* pbNewPayload = new BYTE[cbNewPayload];
+
+ if (pbNewPayload != nullptr) {
+ RtlCopyMemory(pbNewPayload, pbPayload, cbPayload);
+ RtlZeroMemory(pbNewPayload + cbPayload, sizeof(WCHAR));
+
+ HandleReceivedMessage(cbNewPayload, pbNewPayload);
+
+ SAFE_DELETEARRAY(pbNewPayload);
+ }
+ }
+ else {
+ HandleReceivedMessage(cbPayload, pbPayload);
+ }
+ }
+ }
+
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturnVoid();
+}
+
+void CFileObject::HandleReceiveHcePacket(
+ _In_ USHORT uConnectionId,
+ _In_ DWORD cbPayload,
+ _In_reads_bytes_(cbPayload) PBYTE pbPayload
+ )
+{
+ MethodEntry("...");
+
+ size_t cbUsedBufferSize = 0;
+ DWORD cbNewPayload = cbPayload + 2 * sizeof(USHORT);
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsSecureElementManager());
+
+ BYTE* pbNewPayload = new BYTE[cbNewPayload];
+
+ if (pbNewPayload != nullptr) {
+ RtlCopyMemory(pbNewPayload + cbUsedBufferSize, &uConnectionId, sizeof(USHORT));
+ cbUsedBufferSize += sizeof(USHORT);
+ USHORT cbSize = (USHORT)cbPayload;
+
+ RtlCopyMemory(pbNewPayload + cbUsedBufferSize, &cbSize, sizeof(USHORT));
+ cbUsedBufferSize += sizeof(USHORT);
+ RtlCopyMemory(pbNewPayload + cbUsedBufferSize, pbPayload, cbPayload);
+
+ HandleReceivedMessage(cbNewPayload, pbNewPayload);
+
+ SAFE_DELETEARRAY(pbNewPayload);
+ }
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturnVoid();
+}
+
+void CFileObject::HandleSecureElementEvent(SECURE_ELEMENT_EVENT_INFO* pInfo)
+{
+ MethodEntry("...");
+
+ EnterCriticalSection(&m_RoleLock);
+
+ NT_ASSERT(IsSecureElementEvent());
+
+ if ((IsEqualGUID(m_SecureElementId, pInfo->guidSecureElementId) || IsEqualGUID(m_SecureElementId, GUID_NULL)) &&
+ (m_SecureElementEventType == pInfo->eEventType)) {
+ BYTE* pbPayload = (BYTE*)pInfo;
+ DWORD cbPayload = SECURE_ELEMENT_EVENT_INFO_HEADER + pInfo->cbEventData;
+
+ HandleReceivedMessage(cbPayload, pbPayload);
+ }
+
+ LeaveCriticalSection(&m_RoleLock);
+ MethodReturnVoid();
+}
+
+VOID CFileObject::HandleReceivedMessage(
+ _In_ DWORD cbPayload,
+ _In_reads_bytes_(cbPayload) PBYTE pbPayload
+ )
+{
+ bool fDelivered = false;
+
+ if (m_Request != nullptr) {
+ fDelivered = CompleteRequest(cbPayload, pbPayload);
+ }
+
+ if ((!fDelivered) && (m_dwQueueSize < MAX_MESSAGE_QUEUE_SIZE)) {
+ CPayload* pPayload = new CPayload();
+
+ if (pPayload != nullptr) {
+ if (NT_SUCCESS(pPayload->Initialize(cbPayload, pbPayload))) {
+ InsertTailList(&m_Queue, pPayload->GetListEntry());
+ m_dwQueueSize++;
+ }
+ else {
+ delete pPayload;
+ }
+ }
+ }
+}
+
+VOID CFileObject::OnRequestCancel(_In_ WDFREQUEST Request)
+{
+ FunctionEntry("...");
+
+ CFileObject *pFileObject = GetFileObject(WdfRequestGetFileObject(Request));
+
+ NT_ASSERT(pFileObject != nullptr);
+ pFileObject->Cancel();
+
+ FunctionReturnVoid();
+}
+
+void CFileObject::Cancel()
+{
+ MethodEntry("...");
+
+ EnterCriticalSection(&m_RoleLock);
+ CompleteRequest(STATUS_CANCELLED, 0, false);
+ LeaveCriticalSection(&m_RoleLock);
+
+ MethodReturnVoid();
+}
+
+bool
+CFileObject::CompleteRequest(
+ _In_ DWORD cbPayload,
+ _In_reads_bytes_opt_(cbPayload) PBYTE pbPayload
+ )
+{
+ MethodEntry("cbPayload = %d", cbPayload);
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ bool fDelivered = false;
+ WDFMEMORY OutputMemory;
+ size_t cbUsedBufferSize = 0;
+ size_t cbMaxBufferSize = 0;
+
+ NT_ASSERT(m_Request != nullptr);
+
+ Status = WdfRequestRetrieveOutputMemory(m_Request, &OutputMemory);
+
+ if (NT_SUCCESS(Status) && OutputMemory != nullptr) {
+ // Set the first 4 bytes as the size of the payload as a hint for future subscriptions.
+ Status = WdfMemoryCopyFromBuffer(OutputMemory, cbUsedBufferSize, &cbPayload, sizeof(DWORD));
+ cbUsedBufferSize += sizeof(DWORD);
+
+ if (NT_SUCCESS(Status)) {
+ if (pbPayload != nullptr) {
+ WdfMemoryGetBuffer(OutputMemory, &cbMaxBufferSize);
+
+ if (cbMaxBufferSize < (cbPayload + cbUsedBufferSize)) {
+ // We are unable to copy the payload into the output memory,
+ // Returning this signals to the client to send a bigger buffer
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+ else {
+ Status = WdfMemoryCopyFromBuffer(OutputMemory, cbUsedBufferSize, pbPayload, cbPayload);
+ }
+ }
+
+ if (NT_SUCCESS(Status)) {
+ fDelivered = true;
+ cbUsedBufferSize += cbPayload;
+ }
+ }
+
+ if (!CompleteRequest(Status, cbUsedBufferSize, true)) {
+ fDelivered = false;
+ }
+ }
+
+ MethodReturnBool(fDelivered);
+}
+
+
+bool
+CFileObject::CompleteRequest(
+ _In_ NTSTATUS CompletionStatus,
+ _In_ size_t cbSize,
+ _In_ bool fIsCancelable
+ )
+{
+ MethodEntry("CompletionStatus = %!STATUS!, cbSize = %d, fIsCancelable = %!bool!",
+ CompletionStatus, (DWORD)cbSize, fIsCancelable);
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ bool fCompleted = false;
+
+ if (m_Request != nullptr) {
+ if (fIsCancelable) {
+ Status = WdfRequestUnmarkCancelable(m_Request);
+ }
+
+ if (NT_SUCCESS(Status)) {
+ WdfRequestCompleteWithInformation(m_Request, CompletionStatus, cbSize);
+ m_Request = nullptr;
+ fCompleted = true;
+ }
+ }
+
+ MethodReturnBool(fCompleted);
+}
+
+void CFileObject::PurgeQueue()
+{
+ m_dwQueueSize = 0;
+
+ while (!IsListEmpty(&m_Queue)) {
+ delete CPayload::FromListEntry(RemoveHeadList(&m_Queue));
+ }
+} \ No newline at end of file