Getting PID of peer socket on Windows - winapi

I am writing a web proxy and when a request comes in (typically from the browser on the machine), I'd like to also get the pid and any other requesting application.
Is there any way to determine this using Win32?

What you are asking for is only possible if the client and server are running on the same machine.
When a client is connected to the proxy, the proxy can use getpeername() to query the socket for the remote client IP/Port (or use the IP/Port reported by accept()) and getsockname() to get its local server IP/Port. Then the proxy can use GetTcpTable2() (IPv4) or GetTcp6Table2() (IPv6) to retrieve a list of active TCP connections and loop through it looking for a connection that matches the IP/Port pairs. If found, the list entry will tell you the process ID that owns that connection.
For example:
DWORD GetClientPid(SOCKET client)
{
DWORD pid = 0;
sockaddr_in ServerAddr = {0};
int ServerAddrSize = sizeof(ServerAddr);
sockaddr_in ClientAddr = {0};
int ClientAddrSize = sizeof(ClientAddr);
if ((getsockname(client, (sockaddr*)&ServerAddr, &ServerAddrSize) == 0) &&
(getpeername(client, (sockaddr*)&ClientAddr, &ClientAddrSize) == 0))
{
PMIB_TCPTABLE2 TcpTable = NULL;
ULONG TcpTableSize = 0;
ULONG result;
do
{
result = GetTcpTable2(TcpTable, &TcpTableSize, TRUE);
if (result != ERROR_INSUFFICIENT_BUFFER)
break;
LocalFree(TcpTable);
TcpTable = (PMIB_TCPTABLE2) LocalAlloc(LMEM_FIXED, TcpTableSize);
}
while (TcpTable != NULL);
if (result == NO_ERROR)
{
for (DWORD dw = 0; dw < TcpTable->dwNumEntries; ++dw)
{
PMIB_TCPROW2 row = &(TcpTable->table[dw]);
if ((row->dwState == MIB_TCP_STATE_ESTAB) &&
(row->dwLocalAddr == ClientAddr.sin_addr.s_addr) &&
((row->dwLocalPort & 0xFFFF) == ClientAddr.sin_port) &&
(row->dwRemoteAddr == ServerAddr.sin_addr.s_addr) &&
((row->dwRemotePort & 0xFFFF) == ServerAddr.sin_port))
{
pid = row->dwOwningPid;
break;
}
}
}
LocalFree(TcpTable);
}
return pid;
}
SOCKET client = accept(server, NULL, NULL);
if (client != INVALID_SOCKET)
{
DWORD ClientPid = GetClientPid(client);
...
}

Related

rdma-core rsocket server receiver connect fork a new sub process, send return 13 permission denied

Now I am testing rdma-core with fork.
In server I use rselect and raccept to receive rsocket connection from client.
Server can rrecv the msg from client successfully.
But server cannot rsend to client. It return -1 and errno 13 permission denied.
THe same code without rsocket, it will send OK.
For rdma-core/librdmacm rsocket, how support fork?
int main(int argc,char *argv[])
{
int ret;
int server_sockfd, client_sockfd;
int server_len, client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
memset(&server_address,0,sizeof(server_address));
memset(&client_address,0,sizeof(client_address));
int result;
fd_set readfds, testfds;
int maxfd;
int on=1;
server_sockfd = rsocket(AF_INET, SOCK_STREAM, 0);
setsockopt(server_sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(8888);
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
ret = rbind(server_sockfd,(struct sockaddr *)&server_address,sizeof(server_address));
printf("rbind retunr %d\n", ret);
ret = rlisten(server_sockfd,5);
printf("rlisten retunr %d\n", ret);
//set fd_set
FD_ZERO(&readfds);
FD_SET(server_sockfd,&readfds);
maxfd = server_sockfd + 1;
while(1)
{
char ch;
int fd,i;
int nread;
testfds = readfds;
result = rselect(FD_SETSIZE, &testfds, NULL, NULL, NULL);
printf("rselect retunr %d\n", result);
if(result < 1)
{
printf("server5\n");
exit(1);
}
for( fd = 1; fd < maxfd; fd++)
{
if(FD_ISSET(fd,&testfds))
{
if(fd == server_sockfd)
{
memset(&client_address,0,sizeof(client_address));
client_len = sizeof(client_address);
client_sockfd = raccept(server_sockfd,(struct sockaddr *)&client_address,&client_len);
if(fork()==0)
{
setsockopt(client_sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
rclose(server_sockfd);
ret = rrecv(client_sockfd,&ch,1,0);
printf("recv from client : %c, ret %d\n",ch, ret);
sleep(2);
ch++;
ret = rsend(client_sockfd,&ch,1,0);
printf("rsend to client : %c, ret %d, erro %d\n",ch, ret, errno);
}
}
}
}
}
}
As you can see from the rsocket man page:
The preload library can be used by setting LD_PRELOAD when running. Note that not all applications will work with rsockets. Support is limited based on the socket options used by the application. Support for fork() is limited, but available. To use rsockets with the preload library for applications that call fork, users must set the environment variable RDMAV_FORK_SAFE=1 on both the client and server side of the connection. In general, fork is supportable for server applications that accept a connection, then fork off a process to handle the new connection.
As far as I can see, to use rsockets with fork, you have to:
Use sockets API in both your client and your servers (rather than using rsocket calls directly).
Run both the client and your server with these environment variables: RDMAV_FORK_SAFE=1 LD_PRELOAD=/usr/lib/x86_64-linux-gnu/rsocket/librspreload.so (or change the path according to where librspreload.so is installed on your system).
Without using LD_PRELOAD it seems like fork is not supported, meaning that a child process can't use rsockets that the parent has created.

windows socket multithreading I/O Completion ports

I wrote a windows socket server with I/O Completion port to handle heavy data transmission. It works smoothly when there is only one client connected. when more than one client connect, other threads just seem blocked while only one thread works fine.
I changed my design to use select() with each thread for each connection, the problem seems still the same. By the way, the same design works fine in OSX.
However, when I run multiple instances of my process, a process serves each connection, it works great.
Anyone kind enough to enlighten me? I still prefer multithread design. Core code as following:
other part of code basically comes from: http://www.codeproject.com/Articles/13382/A-simple-application-using-I-O-Completion-Ports-an
//Worker thread will service IOCP requests
DWORD WINAPI WorkerThread(LPVOID lpParam)
{
int nThreadNo = (int)lpParam;
void *lpContext = NULL;
OVERLAPPED *pOverlapped = NULL;
CClientContext *pClientContext = NULL;
DWORD dwBytesTransfered = 0;
int nBytesRecv = 0;
int nBytesSent = 0;
DWORD dwBytes = 0, dwFlags = 0;
//Worker thread will be around to process requests, until a Shutdown event is not Signaled.
while (WAIT_OBJECT_0 != WaitForSingleObject(g_hShutdownEvent, 0))
{
BOOL bReturn = GetQueuedCompletionStatus(
g_hIOCompletionPort,
&dwBytesTransfered,
(LPDWORD)&lpContext,
&pOverlapped,
INFINITE);
//WriteToConsole("\nThread %d: GetQueuedCompletionStatus.", nThreadNo);
if (NULL == lpContext)
{
//We are shutting down
break;
}
//Get the client context
pClientContext = (CClientContext *)lpContext;
if ((FALSE == bReturn) || ((TRUE == bReturn) && (0 == dwBytesTransfered)))
{
//Client connection gone, remove it.
RemoveFromClientListAndFreeMemory(pClientContext);
continue;
}
WSABUF *p_wbuf = pClientContext->GetWSABUFPtr();
OVERLAPPED *p_ol = pClientContext->GetOVERLAPPEDPtr();
switch (pClientContext->GetOpCode())
{
case OP_READ:
pClientContext->SetTransLen(dwBytesTransfered);
if(!pClientContext->IsComplete())
{
pClientContext->SetOpCode(OP_READ);
dwFlags = 0;
//Overlapped send
nBytesSent = WSASend(pClientContext->GetSocket(), p_wbuf, 1,
&dwBytes, dwFlags, p_ol, NULL);
WriteToConsole("\nThread %d: WSASend continue bytes = %d.", nThreadNo, dwBytes);
if ((SOCKET_ERROR == nBytesSent) && (WSA_IO_PENDING != WSAGetLastError()))
{
//Let's not work with this client
RemoveFromClientListAndFreeMemory(pClientContext);
WriteToConsole("\nThread %d: WSASend failed.", nThreadNo);
}
}
else
{
WriteToConsole("\nsocket %d: WSASend complete.", pClientContext->GetSocket());
//clear cache
pClientContext->ResetWSABUF();
//for the next recv, must be triggered.
pClientContext->SetOpCode(OP_WRITE);
//Get the data.
nBytesRecv = WSARecv(pClientContext->GetSocket(), p_wbuf, 1,
&dwBytes, &dwFlags, p_ol, NULL);
if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
{
WriteToConsole("\nThread %d: Error occurred while executing WSARecv().", nThreadNo);
//Let's not work with this client
RemoveFromClientListAndFreeMemory(pClientContext);
}
}
break;
case OP_WRITE:
pClientContext->SetTransLen(dwBytesTransfered);
if(pClientContext->IsComplete())
{
if(!pClientContext->ProcessCmd())
{
WriteToConsole("\nThread %d: ProcessCmd failed.", nThreadNo);
//Let's not work with this client
RemoveFromClientListAndFreeMemory(pClientContext);
}
WriteToConsole("\nThread %d: receive completed.", nThreadNo);
//Send the message back to the client.
pClientContext->SetOpCode(OP_READ);
dwFlags = 0;
//Overlapped send
nBytesSent = WSASend(pClientContext->GetSocket(), p_wbuf, 1,
&dwBytes, dwFlags, p_ol, NULL);
WriteToConsole("\nThread %d: WSASend bytes = %d.", nThreadNo, dwBytes);
if ((SOCKET_ERROR == nBytesSent) && (WSA_IO_PENDING != WSAGetLastError()))
{
WriteToConsole("\nThread %d: Error occurred while executing WSASend().", nThreadNo);
//Let's not work with this client
RemoveFromClientListAndFreeMemory(pClientContext);
}
}
else //continue receiving
{
pClientContext->SetOpCode(OP_WRITE);
//Get the data.
nBytesRecv = WSARecv(pClientContext->GetSocket(), p_wbuf, 1,
&dwBytes, &dwFlags, p_ol, NULL);
WriteToConsole("\nThread %d: WSARecv continue bytes = %d.", nThreadNo, dwBytes);
if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
{
WriteToConsole("\nThread %d: Error occurred while executing WSARecv().", nThreadNo);
//Let's not work with this client
RemoveFromClientListAndFreeMemory(pClientContext);
}
}
break;
default:
//We should never be reaching here, under normal circumstances.
break;
} // switch
} // while
return 0;
}
Updates: since my server app need to send large amount of data to multiple clients, it works fine maybe for some minutes, but somehow for no reason, some threads just block without responding. Does it have anything to do with the size of data?

CDhtmlDialog: How to capture event from iframe?

I have a CHtmlDialog with an iframe in it. I can capture onClick event from the button placed in the top level HTML with DHTML_EVENT_ONCLICK macro. But the same method doesn't work with the event from the button placed in the iframe. Is there a way to capture events from iframe?
(By the way, both pages come from resource and they don't have cross-domain policy problem.)
I don't know of a proper solution for this.
The only way I can think of to get this to work is to ping a web endpoint from the iframe using a session id along with event/parameter data and poll that service using the session id from the app.
I'm looking into hosting a local web server within the app to facilitate this.
Edit - My implementation.
The workaround I found was to have the iFrame make a fake network request with attribute data attached as url parameters. I then listen on a thread to the local DNS cache for requests to the fake domain and extract the name value parameters.
Here is a snippet that reads the DNS cache:
UINT CIFrameListener::ListenerThread(LPVOID pParam)
{
THREADPARAM* ts = (THREADPARAM*)pParam;
Util util;
long startScanTime = util.TimeMS();
HINSTANCE dnsApiLib = LoadLibraryA("DNSAPI.dll");
DNS_GET_CACHE_DATA_TABLE dnsGetCacheDataTable = nullptr;
if(dnsApiLib != nullptr) {
dnsGetCacheDataTable = (DNS_GET_CACHE_DATA_TABLE)GetProcAddress(dnsApiLib, "DnsGetCacheDataTable");
}
while(ts->_this->m_running){
int dnsIndex = 0;
// Check DNS
DNSCACHEENTRY entry;
PDNSCACHEENTRY pEntry = &entry;
dnsGetCacheDataTable(pEntry);
pEntry = pEntry->pNext;
while(pEntry != nullptr) {
size_t len = wcslen(pEntry->pszName);
CString domain(pEntry->pszName);
bool allreadyProcessed = false;
for (int i=0; i<ts->_this->messagesSent.size(); i++){
if( domain.Compare(ts->_this->messagesSent[i]) == 0){
allreadyProcessed = true;
}
}
ts->_this->messagesSent.push_back(domain);
if(allreadyProcessed == false){
int end = domain.Find(_T(".infocache.fakedomain.com"));
if(end != -1){
TRACE2(" Domain: %s %d \n", domain, dnsIndex);
int pos = domain.ReverseFind('-');
if(pos != -1){
CString token3 = domain.Mid(pos + 1, end - pos - 1);
domain = domain.Mid(0, pos);
pos = domain.ReverseFind('-');
if(pos != -1){
CString token2 = domain.Mid(pos + 1, domain.GetLength() - pos - 1);
domain = domain.Mid(0, pos);
pos = domain.ReverseFind('-');
if(pos != -1){
CString token1 = domain.Mid(pos + 1, domain.GetLength() - pos - 1);
CString token0 = domain.Mid(0, pos);
int comp = ts->_this->m_session.Compare(token0);
if(comp == 0){
// Notify handler there is a new event.
// notify(token2, token3, token0);
}
}
}
}
dnsIndex++;
}
}
pEntry = pEntry->pNext;
}
Sleep(250);
}
return 1;
}

ConnectEx requires the socket to be "initially bound", but to what?

The ConnectEx function requires an "unconnected, previously bound socket". Indeed, if I omit the bind step in my example (see below), ConnectEx fails with WSAEINVAL.
Here's my current understanding: before calling ConnectEx, bind the socket to INADDR_ANY and port 0 (unless it is already bound):
struct sockaddr_in addr;
ZeroMemory(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = 0;
rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr));
if (rc != 0) { ... bind failed; call WSAGetLastError to see why ... }
Or for an IPv6 socket:
struct sockaddr_in6 addr;
ZeroMemory(&addr, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_addr = in6addr_any;
addr.sin6_port = 0;
rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr));
if (rc != 0) { ... bind failed; call WSAGetLastError to see why ... }
This lets the operating system assign a local address to our socket (as opposed to the remote address we are connecting to). connect does this step automatically, but ConnectEx does not.
My questions are:
Is my assessment correct?
Is there a way to do this automatic bind that is agnostic to the address family, or will I have to handle each of AF_INET, AF_INET6, AF_BTH (Bluetooth), etc. manually?
Working ConnectEx example (also on Gist: https://gist.github.com/4158972):
#include <stdio.h>
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
struct mswsock_s {
LPFN_CONNECTEX ConnectEx;
} mswsock;
static BOOL load_mswsock(void)
{
SOCKET sock;
DWORD dwBytes;
int rc;
/* Dummy socket needed for WSAIoctl */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET)
return FALSE;
{
GUID guid = WSAID_CONNECTEX;
rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid),
&mswsock.ConnectEx, sizeof(mswsock.ConnectEx),
&dwBytes, NULL, NULL);
if (rc != 0)
return FALSE;
}
rc = closesocket(sock);
if (rc != 0)
return FALSE;
return TRUE;
}
int main(int argc, char *argv[])
{
int rc;
BOOL ok;
WSADATA wsaData;
SOCKET sock;
rc = WSAStartup(MAKEWORD(2,2), &wsaData);
if (rc != 0) {
printf("WSAStartup failed: %d\n", rc);
return 1;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
printf("Your computer is from the wrong millenium.\n");
WSACleanup();
return 1;
}
if (!load_mswsock()) {
printf("Error loading mswsock functions: %d\n", WSAGetLastError());
return 1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
printf("socket: %d\n", WSAGetLastError());
return 1;
}
/* ConnectEx requires the socket to be initially bound. */
{
struct sockaddr_in addr;
ZeroMemory(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = 0;
rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr));
if (rc != 0) {
printf("bind failed: %d\n", WSAGetLastError());
return 1;
}
}
/* Issue ConnectEx and wait for the operation to complete. */
{
OVERLAPPED ol;
ZeroMemory(&ol, sizeof(ol));
sockaddr_in addr;
ZeroMemory(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("173.194.37.36"); // google.com
addr.sin_port = htons(80);
ok = mswsock.ConnectEx(sock, (SOCKADDR*) &addr, sizeof(addr), NULL, 0, NULL, &ol);
if (ok) {
printf("ConnectEx succeeded immediately\n");
} else if (WSAGetLastError() == ERROR_IO_PENDING) {
printf("ConnectEx pending\n");
DWORD numBytes;
ok = GetOverlappedResult((HANDLE) sock, &ol, &numBytes, TRUE);
if (ok)
printf("ConnectEx succeeded\n");
else
printf("ConnectEx failed: %d\n", WSAGetLastError());
} else {
printf("ConnectEx failed: %d\n", WSAGetLastError());
return 1;
}
}
/* Make the socket more well-behaved. */
rc = setsockopt(sock, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
if (rc != 0) {
printf("SO_UPDATE_CONNECT_CONTEXT failed: %d\n", WSAGetLastError());
return 1;
}
/* This will fail if SO_UPDATE_CONNECT_CONTEXT was not performed. */
rc = shutdown(sock, SD_BOTH);
if (rc != 0) {
printf("shutdown failed: %d\n", WSAGetLastError());
return 1;
}
printf("Done\n");
return 0;
}
connect does this step automatically, but ConnectEx does not.
Correct.
Is my assessment correct?
Yes.
Is there a way to do this automatic bind that is agnostic to the address family, or will I have to handle each of AF_INET, AF_INET6, AF_BTH (Bluetooth), etc. manually?
I believe that INADDR_ANY is a bunch of zeros in all address families, so you could just try using the memset() and omitting the assignment to addr.sin_addr.s_addr completely. Whether this is kosher, portable, politically correct etc. is another question into which I will not enter.
It seems pretty curious that Microsoft didn't manage to have ConnectEx() call bind() internally, considering that saving system calls is the motivation for its existence, and also considering that most programs never bind an outbound socket at all.
It is possible to get the bind address for ConnectEx in an address family independent way.
Solution 1
Call getaddrinfo with the following options:
pServiceName = "0"
hints.ai_flags = AI_PASSIVE
hints.ai_family = address family of the socket
Then use the first result of the returned address list.
To get the address family of the socket you can use getsockopt with SO_PROTOCOL_INFOW.
Solution 2
Use SOCKADDR_STORAGE for the address structure and call INETADDR_SETANY which is defined in MSTcpIP.h. It supports AF_INET and AF_INET6.

Fail to open SSDP socket on Windows XP if already opened by other application

I want to listen to SSDP multicasts on port 1900. The port is already opened by Windows Discovery Service. I fail to bind my socket despite using SO_REUSEADDR socket option. I'm starting my application as administrator.
If I stop the service, start my application and then restart the service, then both get messages. What am I doing wrong?
static SOCKET CreateSocket(const char *ccAddress, unsigned short ulPort, struct IfPoolItem *item) {
struct sockaddr_in sAddr;
struct ip_mreq mc_req;
SOCKET sRet;
char cSockParam = TRUE;
/* create a socket */
if((sRet = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
{
return(INVALID_SOCKET);
}
item->s = sRet;
if (setsockopt(sRet, IPPROTO_IP, SO_REUSEADDR, &cSockParam, sizeof(cSockParam)) == -1) {
int iTmp = WSAGetLastError();
return (INVALID_SOCKET);
}
/* bind the socket to the given port */
memset(&sAddr, 0, sizeof(sAddr));
sAddr.sin_family = AF_INET;
sAddr.sin_addr.s_addr = inet_addr(item->szIP);
sAddr.sin_port = htons(ulPort);
if(bind(sRet, (struct sockaddr *)&sAddr, sizeof(sAddr)) == SOCKET_ERROR)
{
int iTmp = WSAGetLastError();
closesocket(sRet);
return(INVALID_SOCKET);
}
cSockParam = 4;
if (setsockopt(sRet, IPPROTO_IP, IP_MULTICAST_TTL, &cSockParam, sizeof(cSockParam)) == -1) {
int iTmp = WSAGetLastError();
return (INVALID_SOCKET);
}
/* Construct an IGMP join request structure */
mc_req.imr_multiaddr.s_addr = inet_addr(ccAddress);
mc_req.imr_interface.s_addr = inet_addr(item->szIP);
/* Send an ADD_MEMBERSHIP message via setsockopt */
if((setsockopt(sRet, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char FAR *) &mc_req, sizeof(mc_req))) == -1) {
int iTmp = WSAGetLastError();
return (INVALID_SOCKET);
}
/* Return the created socket. */
return(sRet);
}
I am not an expert with sockets but if you look at this msdn page it says that SO_REUSEADDR should be used with level = SOL_SOCKET parameter. So probably you call should be something like
setsockopt(sRet, SOL_SOCKET, SO_REUSEADDR,&cSockParam, sizeof(cSockParam))
Please try this and let meknow if it made any difference
I am not exactly sure if this is your problem, but the WDS may be using SO_EXCLUSIVEADDRUSE which is preventing your bind(). The docs in this link would seem to describe the behavior you are reporting.

Resources