C++ Pipe [How to get ipaddress or hostname of connected client.] - c++

I wanna get client ip address or host name.
I'm using VS6.0 & VS2010 on Windows 7 Ultimate.
CreateNamedPipe(...) on Server is as follows :
SECURITY_ATTRIBUTES sa = {0};
hPipe = CreateNamedPipe(
_T("\\\\.\\pipe\\AnonymousPipe"), // pipe name
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
0xD000,
0xD000,
NMPWAIT_USE_DEFAULT_WAIT,
&sa);
if (hPipe == INVALID_HANDLE_VALUE)
{
_tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError());
return -1;
}
fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected)
{
printf("Client connected, creating a processing thread.\n");
// Get Information Of Connected Client.
// Client Ip Address or Hostname.
}
How can I detect which client connected to server?
I can see the connection between server and client using netstat cmd line.
netstat -an|find "445"
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING
TCP 0.0.0.0:445 192.168.125.115:4124 ESTABLISHED
TCP 0.0.0.0:445 192.168.125.192:4882 ESTABLISHED
String "192.168.125.115"&"192.168.125.192" is my target.
But I cannot find a way to detect connected client ip in programmatically in VS6.0.
Someone told me that use GetNamedPipeClientComputerName() to solve this problem. But
GetNamedPipeClientComputerName()
Minimum supported client Windows Vista [desktop apps only]
Minimum supported server Windows Server 2008 [desktop apps only]
Many client still using Windows XP.
I have searched a lot to solve the problem, but I could not find the code describing for this.
Thank you for all viewers.

how already answered in comments GetNamedPipeClientComputerName can be used for this. it available from Windows Vista. if want it on xp also it can be implemented in next way:
ULONG AltGetNamedPipeClientComputerName(HANDLE Pipe, PWSTR ClientComputerName, ULONG ClientComputerNameLength)
{
static const char AttributeName[] = "ClientComputerName";
if (HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL))
{
IO_STATUS_BLOCK iosb;
NTSTATUS status = NtFsControlFile(Pipe,
hEvent,
NULL,
NULL,
&iosb,
FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE,
(void*)AttributeName,
sizeof(AttributeName),
ClientComputerName,
ClientComputerNameLength*sizeof(WCHAR));
if (status == STATUS_PENDING)
{
WaitForSingleObject(hEvent, INFINITE);
status = iosb.Status;
}
CloseHandle(hEvent);
return status == STATUS_NOT_FOUND ? ERROR_PIPE_LOCAL : RtlNtStatusToDosError(status);
}
return GetLastError();
}
this is general how really GetNamedPipeClientComputerName implemented internal. however if we use synchronous pipes (so created without FILE_FLAG_OVERLAPPED) we can simplify function - not need create event - NtFsControlFile never return STATUS_PENDING for synchronous handle.
ULONG AltGetNamedPipeClientComputerNameSync(HANDLE Pipe, PWSTR ClientComputerName, ULONG ClientComputerNameLength)
{
static const char AttributeName[] = "ClientComputerName";
IO_STATUS_BLOCK iosb;
NTSTATUS status = NtFsControlFile(Pipe,
hEvent,
NULL,
NULL,
&iosb,
FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE,
(void*)AttributeName,
sizeof(AttributeName),
ClientComputerName,
ClientComputerNameLength*sizeof(WCHAR));
return status == STATUS_NOT_FOUND ? ERROR_PIPE_LOCAL : RtlNtStatusToDosError(status);
}
from another side if we use asynchronous pipe - we can and not wait when FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE completed, but handle it asyncronous like other requests, which you handle async
one very important note - we can not use DeviceIoControl here, but only NtFsControlFile - this is because DeviceIoControl internally check dwIoControlCode and if it for FILE_DEVICE_FILE_SYSTEM call NtFsControlFile otherwise NtDeviceIoControlFile. the FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE defined in ntifs.h (from wdk) as
#define FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE CTL_CODE(FILE_DEVICE_NAMED_PIPE, 12, METHOD_BUFFERED, FILE_ANY_ACCESS)
for FILE_DEVICE_NAMED_PIPE DeviceIoControl call NtDeviceIoControlFile but we need NtFsControlFile exactly.
note that this worked only for server side pipe end (created by CreateNamedPipe) and in case client is remote. otherwise STATUS_NOT_FOUND will be returned from NtFsControlFile. the GetNamedPipeClientComputerName implementation special check for this code and convert it to ERROR_PIPE_LOCAL

Related

Named pipe sometimes doesn't work

So I wrote a C++ application that connects to an Unreal Engine 4 game through a named pipe. 95% of the time it works perfectly, but sometimes it doesn't seem to connect properly. It is very random so it is hard for me to find the problem that is causing this. The Server is created in my application and the Client is created in the UE4 game. And sometimes the UE4 game doesn't connect and displays error 121:
//
// MessageId: ERROR_SEM_TIMEOUT
//
// MessageText:
//
// The semaphore timeout period has expired.
//
#define ERROR_SEM_TIMEOUT 121L
As I said the problem occurs very randomly and I can't seem to find a specific reason that could cause the problem. The pipe is successfully created, I can see this in windows powershell (get-childitem \.\pipe).
I was thinking maybe it has something todo with the pipe settings I use?
This is my code for creating the pipe server:
DWORD erPipeServer::CreatePipeServer() {
// create a SECURITY_ATTRIBUTES structure.
if (!CreatePipeSecurity(&pSa))
{
dwError = GetLastError();
//wprintf(L"CreatePipeSecurity failed w/err 0x%08lx\n", dwError);
Cleanup();
return dwError;
}
// Create the named pipe.
hNamedPipe = CreateNamedPipe(
pipename, // Pipe name.
PIPE_ACCESS_DUPLEX, // The pipe is duplex; both server and
// client processes can read from and
// write to the pipe
PIPE_TYPE_MESSAGE | // Message type pipe
PIPE_READMODE_MESSAGE | // Message-read mode
PIPE_NOWAIT, // Blocking mode is enabled
PIPE_UNLIMITED_INSTANCES, // Max. instances
BUFFER_SIZE, // Output buffer size in bytes
BUFFER_SIZE, // Input buffer size in bytes
NMPWAIT_WAIT_FOREVER, // Time-out interval
pSa // Security attributes
);
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
dwError = GetLastError();
//wprintf(L"Unable to create named pipe w/err 0x%08lx\n", dwError);
Cleanup();
return dwError;
}
//wprintf(L"The named pipe (%s) is created.\n", pipename);
return dwError;
}
And this is my code for creating the client in Unreal Engine 4:
// Try to open the named pipe identified by the pipe name.
while (true)
{
hPipe = CreateFile(
FULL_PIPE_NAME, // Pipe name
GENERIC_READ | GENERIC_WRITE, // Read and write access
0, // No sharing
NULL, // Default security attributes
OPEN_ALWAYS, // Opens existing pipe
0, // Default attributes
NULL // No template file
);
// If the pipe handle is opened successfully ...
if (hPipe != INVALID_HANDLE_VALUE)
{
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Green, FString::Printf(TEXT("The named pipe %d is connected."), FULL_PIPE_NAME));
break;
}
dwError = GetLastError();
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (ERROR_PIPE_BUSY != dwError)
{
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, FString::Printf(TEXT("Unable to open named pipe ------ %d"),dwError));
goto Cleanup;
}
// All pipe instances are busy, so wait for 5 seconds.
if (!WaitNamedPipe(FULL_PIPE_NAME, 5000))
{
dwError = GetLastError();
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, FString::Printf(TEXT("Could not open pipe: 5 second wait timed out. %d"),dwError));
**THE 121 ERROR OCCURED HERE^^**
goto Cleanup;
}
}
Could it be a problem with the pipe settings or something? I do not understand why it works almost all the time, but sometimes not with no clear reason why or when...
Thanks for any help in advance!
Ok, so I think I fixed the problem, maybe not in the best way but it seems to work good enough for my purpose.
After managing to reproduce the problem, I started working with the idea that the Handle could be open or busy ( Thanks to Karsten!!). I could bug the game into showing the 121 error by using windows powershell and running \.\pipe\name (where name is the pipes name). This would open the pipe and the game could not connect anymore, displaying error 121.
How I "fixed" it: Recreating the pipe when no connection is made after a second. Normally in my application when the server is connected the client is already ready. So connection should be immediately. When it isn't, now the pipe is recreated after a second and then it will work. Off-course this is not a clean fix, but I have no idea how the handle normally can open, because normally the only application trying to connect to the pipe is the game...
But anyway it is a decent work-around for the 1 in 30 times the problem occured for (still) some strange reason...
Any other ideas would be appreciated but this is working for now I think :)

Windows Event Viewer holds a lock on my EXE file

I'm curious about something. I'm developing a Windows service and log all the diagnostic events into the Windows Event Log. So when the service is running I open the Event Viewer (from Administrative tools) to view the results of my service's operation.
This works great except for the moment when I need to uninstall my program (again, for the testing purposes.) For some weird reason the Event Viewer holds a lock on the .exe image file for my service so the uninstaller fails to delete it with the error code ERROR_SHARING_VIOLATION:
The process cannot access the file because it is being used by another process.
This happens only on Vista and later OS and seems not to be an issue on XP.
Any idea how to make Event Viewer release the file lock? (I'm asking about programmatic approach. I can obviously close it manually, but that's not what I'm after.)
I released the lock this way:
Start -> Services
Locate Windows Event Log
Right click -> Restart
There's a less known feature introduced in Vista called Restart Manager that can help you release file locks via a user-mode code. Since you tagged it as C++, based on this article here's a small code sample to do that:
#include <RestartManager.h>
#pragma comment(lib ,"Rstrtmgr.lib")
BOOL ReleaseFileLock(LPCTSTR pFilePath)
{
BOOL bResult = FALSE;
DWORD dwSession;
WCHAR szSessionKey[CCH_RM_SESSION_KEY+1] = { 0 };
DWORD dwError = RmStartSession(&dwSession, 0, szSessionKey);
if (dwError == ERROR_SUCCESS)
{
dwError = RmRegisterResources(dwSession, 1, &pFilePath,
0, NULL, 0, NULL);
if (dwError == ERROR_SUCCESS)
{
UINT nProcInfoNeeded = 0;
UINT nProcInfo = 0;
RM_PROCESS_INFO rgpi[1];
DWORD dwReason;
dwError = RmGetList(dwSession, &nProcInfoNeeded,
&nProcInfo, rgpi, &dwReason);
if (dwError == ERROR_SUCCESS ||
dwError == ERROR_MORE_DATA)
{
if(nProcInfoNeeded > 0)
{
//If current process does not have enough privileges to close one of
//the "offending" processes, you'll get ERROR_FAIL_NOACTION_REBOOT
dwError = RmShutdown(dwSession, RmForceShutdown, NULL);
if (dwError == ERROR_SUCCESS)
{
bResult = TRUE;
}
}
else
bResult = TRUE;
}
}
}
RmEndSession(dwSession);
SetLastError(dwError);
return bResult;
}
I just met same problem. The DLL was locked by svchost.exe process (Windows Audio, DHCP Client, Windows Event Log, TCP/IP NetBIOS Helper, Security Center, Task Scheduler)
Solution: close Event Viewer! :)

Non-blocking ConnectNamedPipe event not getting signaled

I am starting out with named pipes and need to use them for IPC between two local processes. I have both the server and client process using the pipe in nonblocking overlapped mode.
Everything works fine (the server successfully receives a string sent by the client) except that the event passed to ConnectNamedPipe() via the OVERLAPPED structure is not getting signaled as expected (signaled when a client connects).
While the server is blocked on the WaitForSingleObject() call the client process connected to the pipe, sent its data and terminated yet the event does not get signaled. What am I missing?
Server code:
HANDLE hPipe = ::CreateNamedPipeW(
L"\\\\.\\pipe\\ThePipe",
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT,
1,
4096,
4096,
100,
nullptr);
OVERLAPPED ov = {0};
ov.hEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr);
BOOL retVal = ::ConnectNamedPipe(hPipe, &ov);
if (retVal == 0)
{
DWORD err = ::GetLastError();
if (err == ERROR_IO_PENDING)
{
::WaitForSingleObject(ov.hEvent, 30000);
}
if (err == ERROR_PIPE_LISTENING)
{
::WaitForSingleObject(ov.hEvent, 30000); // this blocks until time-out???
}
}
You shouldn't use (deprecated) PIPE_NOWAIT together with overlapped mode. PIPE_NOWAIT makes ConnectNamedPipe immediately return ERROR_PIPE_LISTENING if no client is connected; overlapped I/O just doesn't happen, and waiting for event is useless.
Either you set PIPE_NOWAIT and poll the pipe periodically until success, or you set FILE_FLAG_OVERLAPPED and use an event to check/wait for completion.

Invalid handle error when connecting to COM port higher than 10 on Windows 7

I have developed a simple serial port application that works fine with COM ports lower than 10 (COM9, COM8, ... COM1). But when my device is attached on a port higher than 10, such as COM11, it doesn't connect and I get an INVALID_HANDLE. My code is:
comport = CreateFileA(comPortName.toAscii(), GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(comport == INVALID_HANDLE_VALUE)
{
//Write exception code here
connectionStatus = CONNECTION_STATUS_DISCONNECTED;
}
What's wrong with my code? Is there any solution?
To reach the COM Ports >= 10 you can use the driver's symbolic link. For example, for COM10 it is \\\\.\\COM10.
Just try:
comport = CreateFileA("\\\\.\\COM10", GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(comport == INVALID_HANDLE_VALUE)
{
// Write exception code here
connectionStatus = CONNECTION_STATUS_DISCONNECTED;
}
Of course, this also works for COM ports < 10.
You need to prepend "\\.\" to the COM port name, so it should be something like:
CreateFileA("\\\\.\\COM10", ... )
Source: HOWTO: Specify Serial Ports Larger than COM9

GetQueuedCompletionStatus can't dequeue IO from IOCP if the thread which originally issued the IO is blocking in ReadFile under windows 8

My app stop working after switching to windows 8. I spend hours to debug the problem, found out IOCP behave differently between windows 8 and previous versions. I extract the necessary code to demonstrate and reproduce the problem.
SOCKET sListen;
DWORD WINAPI WorkerProc(LPVOID lpParam)
{
ULONG_PTR dwKey;
DWORD dwTrans;
LPOVERLAPPED lpol;
while(true)
{
GetQueuedCompletionStatus((HANDLE)lpParam, &dwTrans, &dwKey, (LPOVERLAPPED*)&lpol, WSA_INFINITE);
printf("dequeued an IO\n");
}
}
DWORD WINAPI StartProc(LPVOID lpParam)
{
WSADATA WsaData;
if (WSAStartup(0x202,&WsaData)!=0) return 1;
sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN si;
ZeroMemory(&si,sizeof(si));
si.sin_family = AF_INET;
si.sin_port = ntohs(1999);
si.sin_addr.S_un.S_addr = INADDR_ANY;
if(bind(sListen, (sockaddr*)&si, sizeof(si)) == SOCKET_ERROR) return 1;
listen(sListen, SOMAXCONN);
HANDLE hCompletion = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
CreateIoCompletionPort((HANDLE)sListen, hCompletion, (DWORD)0, 0);
CreateThread(NULL, 0, WorkerProc, hCompletion, 0, NULL);
return 0;
}
DWORD WINAPI AcceptProc(LPVOID lpParam)
{
DWORD dwBytes;
LPOVERLAPPED pol=(LPOVERLAPPED)malloc(sizeof(OVERLAPPED));
ZeroMemory(pol,sizeof(OVERLAPPED));
SOCKET sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
BOOL b = AcceptEx(sListen,
sClient,
malloc ((sizeof(sockaddr_in) + 16) * 2),
0,
sizeof(sockaddr_in) + 16,
sizeof(sockaddr_in) + 16,
&dwBytes,
pol);
if(!b && WSAGetLastError() != WSA_IO_PENDING) return 1;
HANDLE hPipe=CreateNamedPipeA("\\\\.\\pipe\\testpipe",PIPE_ACCESS_DUPLEX,PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,PIPE_UNLIMITED_INSTANCES,4096,4096,999999999,NULL);
BYTE chBuf[1024];
DWORD cbRead;
CreateFileA("\\\\.\\pipe\\testpipe", GENERIC_READ |GENERIC_WRITE, 0,NULL, OPEN_EXISTING, 0, NULL);
ReadFile(hPipe,chBuf,1024, &cbRead,NULL);
return 0;
}
int main()
{
printf ("Starting server on port 1999...");
WaitForSingleObject(CreateThread(NULL, 0, StartProc, NULL, 0, NULL),INFINITE);
CreateThread(NULL, 0,AcceptProc, NULL, 0, NULL);
printf ("done\n");
Sleep(10000000);
return 0;
}
This program listen on port 1999 and issue an async accpet then reading a blocking pipe. I have tested this program on Windows 7, 8, XP, 2003, 2008, after "telnet 127.0.0.1 1999", "dequeued an IO\n" will printed on console except windows 8.
The point is the thread which originally issued the async operation must not blocking in ReadFile or GetQueuedCompletionStatus will never dequeue that IO until ReadFile returns on windows 8.
I also tested using "scanf" instead of reading pipe, the results are same since "scanf" will call ReadFile to read console eventually. I don't know if ReadFile is the only function affected or there may be other functions.
What I can think of is using a dedicated thread to issue async operations, and all business logic communicate with that dedicated thread to perform accept/send/recv. But extra layer means extra overhead, is there any way to achieve the same performance as previous versions of windows on windows 8?
See https://connect.microsoft.com/WindowsServer/feedback/details/760161/breaking-change-to-acceptex-and-iocp-in-server-2012-and-windows-8
This is a bug and the official MS response is "We've passed this to the base OS team and they will consider this for a future update. I'm resolving this postponed."
Note: I ran this test on a fully patched version of Windows 8 today (12th Sep 2013) in preparation to testing Windows 8.1 and found that the problem appears to be fixed on Windows 8 now. I've no idea WHEN it was fixed.