1// winpipes.cpp - written and placed in the public domain by Wei Dai
2
3#include "pch.h"
4#include "winpipes.h"
5
6#ifdef WINDOWS_PIPES_AVAILABLE
7
8#include "wait.h"
9
10NAMESPACE_BEGIN(CryptoPP)
11
12WindowsHandle::WindowsHandle(HANDLE h, bool own)
13	: m_h(h), m_own(own)
14{
15}
16
17WindowsHandle::~WindowsHandle()
18{
19	if (m_own)
20	{
21		try
22		{
23			CloseHandle();
24		}
25		catch (...)
26		{
27		}
28	}
29}
30
31bool WindowsHandle::HandleValid() const
32{
33	return m_h && m_h != INVALID_HANDLE_VALUE;
34}
35
36void WindowsHandle::AttachHandle(HANDLE h, bool own)
37{
38	if (m_own)
39		CloseHandle();
40
41	m_h = h;
42	m_own = own;
43	HandleChanged();
44}
45
46HANDLE WindowsHandle::DetachHandle()
47{
48	HANDLE h = m_h;
49	m_h = INVALID_HANDLE_VALUE;
50	HandleChanged();
51	return h;
52}
53
54void WindowsHandle::CloseHandle()
55{
56	if (m_h != INVALID_HANDLE_VALUE)
57	{
58		::CloseHandle(m_h);
59		m_h = INVALID_HANDLE_VALUE;
60		HandleChanged();
61	}
62}
63
64// ********************************************************
65
66void WindowsPipe::HandleError(const char *operation) const
67{
68	DWORD err = GetLastError();
69	throw Err(GetHandle(), operation, err);
70}
71
72WindowsPipe::Err::Err(HANDLE s, const std::string& operation, int error)
73	: OS_Error(IO_ERROR, "WindowsPipe: " + operation + " operation failed with error 0x" + IntToString(error, 16), operation, error)
74	, m_h(s)
75{
76}
77
78// *************************************************************
79
80WindowsPipeReceiver::WindowsPipeReceiver()
81	: m_resultPending(false), m_eofReceived(false)
82{
83	m_event.AttachHandle(CreateEvent(NULL, true, false, NULL), true);
84	CheckAndHandleError("CreateEvent", m_event.HandleValid());
85	memset(&m_overlapped, 0, sizeof(m_overlapped));
86	m_overlapped.hEvent = m_event;
87}
88
89bool WindowsPipeReceiver::Receive(byte* buf, size_t bufLen)
90{
91	assert(!m_resultPending && !m_eofReceived);
92
93	HANDLE h = GetHandle();
94	// don't queue too much at once, or we might use up non-paged memory
95	if (ReadFile(h, buf, UnsignedMin((DWORD)128*1024, bufLen), &m_lastResult, &m_overlapped))
96	{
97		if (m_lastResult == 0)
98			m_eofReceived = true;
99	}
100	else
101	{
102		switch (GetLastError())
103		{
104		default:
105			CheckAndHandleError("ReadFile", false);
106		case ERROR_BROKEN_PIPE:
107		case ERROR_HANDLE_EOF:
108			m_lastResult = 0;
109			m_eofReceived = true;
110			break;
111		case ERROR_IO_PENDING:
112			m_resultPending = true;
113		}
114	}
115	return !m_resultPending;
116}
117
118void WindowsPipeReceiver::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack)
119{
120	if (m_resultPending)
121		container.AddHandle(m_event, CallStack("WindowsPipeReceiver::GetWaitObjects() - result pending", &callStack));
122	else if (!m_eofReceived)
123		container.SetNoWait(CallStack("WindowsPipeReceiver::GetWaitObjects() - result ready", &callStack));
124}
125
126unsigned int WindowsPipeReceiver::GetReceiveResult()
127{
128	if (m_resultPending)
129	{
130		HANDLE h = GetHandle();
131		if (GetOverlappedResult(h, &m_overlapped, &m_lastResult, false))
132		{
133			if (m_lastResult == 0)
134				m_eofReceived = true;
135		}
136		else
137		{
138			switch (GetLastError())
139			{
140			default:
141				CheckAndHandleError("GetOverlappedResult", false);
142			case ERROR_BROKEN_PIPE:
143			case ERROR_HANDLE_EOF:
144				m_lastResult = 0;
145				m_eofReceived = true;
146			}
147		}
148		m_resultPending = false;
149	}
150	return m_lastResult;
151}
152
153// *************************************************************
154
155WindowsPipeSender::WindowsPipeSender()
156	: m_resultPending(false), m_lastResult(0)
157{
158	m_event.AttachHandle(CreateEvent(NULL, true, false, NULL), true);
159	CheckAndHandleError("CreateEvent", m_event.HandleValid());
160	memset(&m_overlapped, 0, sizeof(m_overlapped));
161	m_overlapped.hEvent = m_event;
162}
163
164void WindowsPipeSender::Send(const byte* buf, size_t bufLen)
165{
166	DWORD written = 0;
167	HANDLE h = GetHandle();
168	// don't queue too much at once, or we might use up non-paged memory
169	if (WriteFile(h, buf, UnsignedMin((DWORD)128*1024, bufLen), &written, &m_overlapped))
170	{
171		m_resultPending = false;
172		m_lastResult = written;
173	}
174	else
175	{
176		if (GetLastError() != ERROR_IO_PENDING)
177			CheckAndHandleError("WriteFile", false);
178
179		m_resultPending = true;
180	}
181}
182
183void WindowsPipeSender::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack)
184{
185	if (m_resultPending)
186		container.AddHandle(m_event, CallStack("WindowsPipeSender::GetWaitObjects() - result pending", &callStack));
187	else
188		container.SetNoWait(CallStack("WindowsPipeSender::GetWaitObjects() - result ready", &callStack));
189}
190
191unsigned int WindowsPipeSender::GetSendResult()
192{
193	if (m_resultPending)
194	{
195		HANDLE h = GetHandle();
196		BOOL result = GetOverlappedResult(h, &m_overlapped, &m_lastResult, false);
197		CheckAndHandleError("GetOverlappedResult", result);
198		m_resultPending = false;
199	}
200	return m_lastResult;
201}
202
203NAMESPACE_END
204
205#endif
206