Bryant
Bryant

Reputation: 324

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

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.

Upvotes: 0

Views: 805

Answers (1)

RbMm
RbMm

Reputation: 33744

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

Upvotes: 1

Related Questions