Resume completion port notification after they were stopped - c++

In the MSDN doc for the lpOverlapped parameter of GetQueuedCompletionStatus it is said that the application can prevent completion port notification by setting the low-order bit of the hEvent member of the OVERLAPPED structure. But is it possible to resume the notifications after they were stopped?
I need to use this for monitoring network folders for changes:
When GetQueuedCompletionStatus returns FALSE and GetLastError() returns ERROR_NETNAME_DELETED, I do this (works):
di->Overlapped.hEvent = CreateEvent( NULL, FALSE, FALSE, di->lpszDirName );
reinterpret_cast<uintptr_t &>(di->Overlapped.hEvent) |= 0x1;
And when the network problem was resolved, I tried to do the reverse operation - but it DID NOT work:
reinterpret_cast<uintptr_t &>(di->Overlapped.hEvent) &= ~(0x1);
(It will be good if the solution be compatible with Windows 7)

first of all completion port notification can not be "suspended" or "resumed"
Even if you have passed the function a file handle associated with a
completion port and a valid OVERLAPPED structure, an application can
prevent completion port notification. This is done by specifying a
valid event handle for the hEvent member of the OVERLAPPED structure,
and setting its low-order bit. A valid event handle whose low-order
bit is set keeps I/O completion from being queued to the completion
port.
this mean the next - when we call some win32 I/O api (api which take pointer to OVERLAPPED as in/out parameter, such as ReadFile, ReadDirectoryChangesW, LockFileEx etc) and file handle (passed to this api) associated with a completion port - despite this we can prevent completion port notification for this call by event handle with low-order bit. this is for only concrete api call and not affect any another api calls. and all this unrelated to GetQueuedCompletionStatus
(strictly said we can simply pass 1 in place hEvent too. but in this case question - how we get notify about I/O complete, if api return pending status ? yes possible wait and on file handle only, call GetOverlappedResult. but this will be correct only in no any another I/O call on this file in concurent)
in any case need understand how this is internally work. all native I/O api have the next signature:
NTSTATUS NTAPI SomeIoApi(
_In_ HANDLE FileHandle,
_In_opt_ HANDLE Event,
_In_opt_ PIO_APC_ROUTINE ApcRoutine,
_In_opt_ PVOID ApcContext,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
...
);
all have this common 5 parameters at begin. for queue I/O completion as result of this call several conditions must be met. of course FileHandle must be associated with some completion port (to this port and can be packet sent). but else one mandatory condition - ApcContext must be not zero (ApcContext != 0). if this 2 condition met and device return not error status (if FILE_SKIP_COMPLETION_PORT_ON_SUCCESS set on file - must be pending status only) - when I/O complete - ApcContext pointer will be pushed to port. and then it can be removed by
NTSTATUS
NTAPI
NtRemoveIoCompletion(
_In_ HANDLE IoCompletionHandle,
_Out_ PVOID *KeyContext,
_Out_ PVOID *ApcContext,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_opt_ PLARGE_INTEGER Timeout
);
or by it win32 shell GetQueuedCompletionStatus.
so solution for not sent packet to port (even is file handle associated with completion port) - set ApcContext = 0. win32 layer do this in next way (pseudo - code):
BOOL WINAPI SomeWin32Api(
HANDLE FileHandle,
LPOVERLAPPED lpOverlapped,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)
{
HANDLE hEvent = lpOverlapped->hEvent;
PVOID ApcContext = lpOverlapped;
if ((ULONG_PTR)hEvent & 1)
{
reinterpret_cast<uintptr_t&>(hEvent) &= ~1;
ApcContext = 0;
}
NTSTATUS status = SomeIoApi(
FileHandle,
hEvent,
lpCompletionRoutine, // not exactly, but by sense
ApcContext,
(PIO_STATUS_BLOCK)lpOverlapped,...);
}
it check low-order bit of hEvent in OVERLAPPED - if it set - pass 0 inplace ApcContext otherwise pass lpOverlapped (pointer to OVERLAPPED) as context ( ApcContext = lpOverlapped;)
note that nt layer let pass any void* pointer as ApcContext. but win32 layer always pass here pointer to OVERLAPPED structure or 0. because this and GetQueuedCompletionStatus return this pointer back as _Out_ LPOVERLAPPED *lpOverlapped (compare with NtRemoveIoCompletion - return as _Out_ PVOID *ApcContext)
anyway this trick affect only concrete single win32 I/O call, and if you late reset low-order bit in hEvent from overlapped ( reinterpret_cast<uintptr_t &>(di->Overlapped.hEvent) &= ~(0x1);) this already can not have any effect - the 0 in place ApcContext already passed.
also from general view this is rarely when we associate file handle with a completion port, but want not use it in some call. usually this is another api call. for example we can create asynchronous file handle, associate it with a completion port. and use port notifications in call WriteFile, but before begin write we can set/remove compression on file via FSCTL_SET_COMPRESSION. because file is asynchronous, the FSCTL_SET_COMPRESSION also can complete asynchronous, but we can want prevent completion port notification for this ioctl, instead wait inplace (on event) for it complete. for such situation and can be used this trick.
and in most case applications (if this not server with huge count of i/o requests) can instead manual call GetQueuedCompletionStatus, bind callback to file via BindIoCompletionCallback or CreateThreadpoolIo. as result system for you create iocp, thread pool which will be listen on this iocp (via GetQueuedCompletionStatus or NtRemoveIoCompletion) and then call your callback. this is very simplify your src code and logic
findings:
i almost sure (despite not view your code) that you not need at all
use trick with event low-order bit
if you use this trick in some I/O request (say ReadDirectoryChangesW)
this affect only this particular request
you can not change the behaviour by reset low-order bit in event
handle after request is sent, or by any another way
you in general not need use GetQueuedCompletionStatus and self thread
pool at all. instead simply call BindIoCompletionCallback for file

Related

How RpcServerInqCallAttributes knows what ClientPID to retrieve

I am reading about the function RpcServerInqCallAttributes (source).
I saw a program (vmcompute.exe) that calls this function like that (based on reversing):
RPC_CALL_ATTRIBUTES CallAttributes;
memset(&CallAttributes, 0, sizeof(CallAttributes));
CallAttributes.Version = 3;
CallAttributes.Flags = RPC_QUERY_CLIENT_PID;
Status = RpcServerInqCallAttributes(0, &ClientContextAttributes);
It retrieves the PID of dockerd but how it knows what PID to retrieve?
It just used the RPC_QUERY_CLIENT_PID to query the PID, but based on what? It didn't specify the name of the process.
I read this answer but it didn't explain how it knows what process PID to retrieve.
Both RpcServerInqCallAttributes and its lower-level counterpart I_RpcBindingInqLocalClientPID have RPC_BINDING_HANDLE Binding as their first parameter:
RPC_STATUS RpcServerInqCallAttributes(
[in] RPC_BINDING_HANDLE ClientBinding,
[in, out] void *RpcCallAttributes
);
RPC_STATUS I_RpcBindingInqLocalClientPID(
[in, optional] RPC_BINDING_HANDLE Binding,
[out] unsigned long *Pid
);
Local RPC requests are usually sent over ALPC, where each message delivered contains both the data payload and LPC/ALPC protocol header, described by PORT_MESSAGE structure. This header has ClientId field, which has both senders PID and TID.
Upon receiving an ALPC request the RPC runtime inside the server process saves these values in the RPC_BINDING_HANDLE object, where they can be retrieved from.
Passing a NULL handle to RpcServerInqCallAttributes/I_RpcBindingInqLocalClientPID means use the current thread's active binding, in which case these APIs take the RPC_BINDING_HANDLE pointer from the current thread's TEB, IIRC via ReservedForNtRpc field.

WSARecv Detour hook crypt

I'm writing a packet encryption for a friends gameserver.
Client is using ws2_32 recv/send but server uses WSARecv/WSASend.
I've managed to encrypt/decrypt send/recv/WSASend, but WSARecv seems impossible.
I'm using the same method as on recv, but it doesn't seem to work.
int WINAPI MyWSARecv(SOCKET socket, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags,LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
LPWSABUF buffers = lpBuffers;
int ret = pWSARecv(socket, buffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);
cryptPacket(buffers->buf, buffers->len);
lpBuffers = buffers;
return ret;
}
Any ideas would be appreciated.
A handful of things to consider.
You are not actually checking the return value from pWASRecv before calling your cryptPacket function. You need to start with that fix first before any other assumptions can be made about how the detour hooks work with the socket code. If the call indicates an error, it's probably not well defined what buffers->len is going to be or what is going to be in those buffers.
Also, you are likely assuming that the socket was initialized to be synchronous. If the socket was initialized for overlapped I/O, then the lpOverlapped and lpCompletionRoutine parameters become very relevant. You may need to hook the completion routine or WSAGetOverlappedResult to actually intercept the socket data.
Finally, may I suggest another approach. Rather than trying to "detour" the socket API calls, run a "proxy socket". That is, when the server socket is created (via a call to "socket"), you change it to listen on a different port. Then you create a separate listen socket that listens on the original port. When an incoming connection comes into your socket, you make a separate "proxy" connection to the actual port the game server is listening on. You can have a dedicated thread that just calls send and recv on the client socket that encrypts/decrypts data as needed.

Assynchronous serial comms: why does ReadFile() set the event in the OVERLAPPED struct?

I have drawn on various sources to piece together some (multi-threaded) code to read and write from/to a serial port. It all works fine... except that the loop in the thread that does the reading from the serial port unintentionally does a busy wait. Essentially what happens repeatedly is:
An event (created outside the read loop) is reset, and its handle used as the hEvent member in an OVERLAPPED struct.
ReadFile() is passed the OVERLAPPED struct (among other parameters) and returns immediately
WaitForSingleObject() waits on the event in the OVERLAPPED struct, but always returns immediately because the event is always set after the ReadFile()
GetOverlappedResult() is then passed the same OVERLAPPED struct, returns successfully, but typically only reads 0 bytes
My expectation was that the whole point of the event was to signal when there is data available to read. But ReadFile() sets the event, and so what is the point? What am I missing?
The following stripped-back code demonstrates the issue on my system (I have COM3 connected). The full code quite happily reads and writes... but the reader suffers from the condition described above:
HANDLE portHandle = CreateFile( "COM3",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL ); // succeeds
HANDLE readerEvent = CreateEvent( 0, TRUE, FALSE, _T( "Rx Event" ) ); // succeeds
char buffer[ 200 ];
DWORD bytesRead;
OVERLAPPED reader;
memset( &reader, 0, sizeof( reader ) );
reader.hEvent = readerEvent;
ResetEvent( readerEvent );
ReadFile( portHandle, buffer, 200, &bytesRead, &reader );
if ( WaitForSingleObject( reader.hEvent, 2000 ) == WAIT_OBJECT_0 )
{
// always true, never has to wait on the event.
}
Found it: the documentation for the ReadFile function contains this paragraph:
When reading from a communications device, the behavior of ReadFile is determined by the current communication time-out as set and retrieved by using the SetCommTimeouts and GetCommTimeouts functions. Unpredictable results can occur if you fail to set the time-out values. For more information about communication time-outs, see COMMTIMEOUTS.
I was not using SetCommTimeouts() at all. Performing a GetCommTimeouts() and inspecting the results showed the port's settings were the values described in this paragraph from the COMMTIMEOUTS documentation:
A value of MAXDWORD, combined with zero values for both the ReadTotalTimeoutConstant and ReadTotalTimeoutMultiplier members, specifies that the read operation is to return immediately with the bytes that have already been received, even if no bytes have been received.
You can also use the WaitCommEvent function to wait on the event with a specific event mask. Code sample using CreateFile and WaitCommEvent: Monitoring Communications Events.

WSARecv sometimes return "invalid handle (error no 6)" for a socket associated with an IOCP port. (C++)

I'm trying to write a server which can support many clients connections simultaneously so I'm trying to do it with IOCP. So let me brief about my code flow and then I can explain my problem. First of all, server is opening a port for listening and waiting on an "accept" call for new incoming connections. For reference I have used same code as mentioned here So it accepts every new incoming connection and returns a new socket descriptor (sd), and then it marks as nonblocking with:
arg = 1;
ioctlsocket(sd, FIONBIO, &arg);
and then enable TCP_NODELAY:
level = IPPROTO_TCP;
optName = TCP_NODELAY;
value = 1;
setsockopt(sd, level, optName, (const char*)&value, sizeof(value));
thereafter associating with an IOCP port as:
CreateIoCompletionPort((HANDLE)sd, iocp_port, (DWORD)completion_key, 4);
completion_key is a class object which is nothing but a container, it contains data buffer, overlapped-buffer, query-type recv/send etc.
and in last issuing a read call:
WSARecv(sd, wsabuf, 1, &bytes, &flags, overlapped, NULL);
wsabuf and overlapped are part of completion_key object.
In 90% cases it works fine i.e. when there is some incoming data available on this socket "GetQueuedCompletionStatus" gets unblocked and it has valid data. But sometimes WSARecv call returns with an error and GetLastError() returns 6 which is "invalid handle" error. I'm bit bewildered why it's happening so.
The way I'm creating an iocp port:
iocp_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
and there are threads which are waiting on "GetQueuedCompletionStatus".
I monitored all system calls which were happening in background. WSARecv internally calls NtDeviceIoControlFile and there is an argument "Event" which is same as what is passed in lpOverlapped structure of WSARecv as hEvent. I wasn't setting hEvent to NULL, so it was taking some garbage value, when it was NULL then NtDeviceIoControlFile returned successfully and for other cases it returned "INVALID_HANDLE" error. Unfortunately, it was NULL most of the time.

Wait for data on COM port?

I'm looking for a way to get a Windows serial port to timeout until it has received data. It would be nice if there was some kind of event that triggered or a function to do exactly what I want.
This is my current implementation.
void waitforCom(unsinged char byte)
{
while (true)
{
ClearCommError(serial_handle, &errors, &status);
if (status.cbInQue>0)
{
//check if correct byte
break;
}
}
}
Another API call you could be using is WaitCommEvent().
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363479(v=vs.85).aspx
This call can work asynchronously since it takes an OVERLAPPED object as a parameter. In your case you'd want to simply wait on the EV_RXCHAR event to let you know data has arrived:
OVERLAPPED o = {0};
o.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
SetCommMask(comPortHandle, EV_RXCHAR);
if (!WaitCommEvent(comPortHandle, &commEvent, &o))
{
// Check GetLastError for ERROR_IO_PENDING, if I/O is pending then
// use WaitForSingleObject() to determine when `o` is signaled, then check
// the result. If a character arrived then perform your ReadFile.
}
Alternatively you could do the same thing by having a thread with an outstanding ReadFile call, but using the OVERLAPPED object instead of blocking as MSalters recommends.
I'm not really a specialist when it comes to WinApi, but there's a whole article on the Microsoft Developer Network, that covers the subject of serial communications. The article mentions the subject of waiting for the data from a port, and it's supplied with an example.
At the winAPI level, for most applications you need to dedicate a thread to serial port input because ReadFile is a blocking call (but with a timeout). The most useful event you can get is having ReadFile return. Just put ReadFile in a loop in a thread and generate your own event or message to some other thread when ReadFile gets some data.