Win32 API: Passing Socket with IPC method
Hi. In this post I talk to you how to correctly pass a socket created in a parent process to a child process in Microsoft 9x systems.
If you have ever written a multi-process concurrent server in a Unix environment, you may have noticed that the passage of the socket between parent and son processes takes place directly. That is, the child inherits the variables of his parent, also including the file descriptor associated with the socket.
An example of this is following:
...
int fd_server, fd_client;
int p_pid;
fd_server = socket(...);
...
fd_client = accept(...);
...
p_pid = fork();
switch(p_pid) {
case 0:
// we are in the child: we can use directly the client/server socket without any problem
close(fd_server);
send(fd, ... );
...
...
}
In Microsoft systems, the procedure is little bit different (especially in 9x systems).
As we can read from the msdn documentation (link):
Under Windows NT and Windows 2000, socket handles are inheritable by default. This feature is often used by a process that wants to spawn a child process and have the child process interact with the remote application on the other end of the connection. It is also common practice on Windows NT to set the standard handles (standard input, output, or error) of the child process to the socket handle. In such cases, the child process usually does not know that its standard handles are actually sockets. Windows 9x differs from Windows NT/Windows 2000 in the following manner:
- Socket handles are not inheritable when created. To ensure that a child process can obtain and use a socket handle created in the parent, the handle must be explicitly duplicated using the Win32 API DuplicateHandle. Set the bInheritHandle parameter of the API to TRUE.
- Socket handles cannot be set to the standard handles of the child process. A programmer may use other mechanisms to pass the socket handle to the client, such as passing the handle values as command line arguments so that the child process can simply look at its argument vector.
So we have to duplicate the client socket in order to use it (in Windows 9x).
In the following source code, I’ll show you how to create a new son process and how to pass the socket to it, so that it can manage independently the connection with the client.
int WindowsProcess(SOCKET sock_client) {
BOOL ret = 0;
HANDLE hPipe;
DWORD dwBytes;
PROCESS_INFORMATION piProc;
STARTUPINFO siStartInfo;
WSAPROTOCOL_INFO protInfo;
OVERLAPPED ol = {0,0,0,0,NULL};
/* I create a named pipe for communication with the spawned process */
hPipe = CreateNamedPipe(
PIPE_NAME, // pipe name
PIPE_ACCESS_DUPLEX | // read/write access
FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | // message type pipe
PIPE_READMODE_BYTE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
PIPE_TIMEOUT_CONNECT, // client time-out
NULL); // default security attribute
if ( hPipe == INVALID_HANDLE_VALUE ) {
return -1;
}
GetStartupInfo(&siStartInfo);
/* I create a new process calling the "test.exe" executable */
ret = CreateProcess("test.exe", NULL,
NULL, NULL, /* security attributes process/thread */
TRUE, /* inherit handle */
0, /* fdwCreate */
NULL, /* lpvEnvironment */
".", /* lpszCurDir */
&siStartInfo, /* lpsiStartInfo */
&piProc);
if ( ret == 0 ) {
return -1;
}
/* I duplicate the socket */
ret = WSADuplicateSocket(sock_client, piProc.dwProcessId, &protInfo);
if ( ret == SOCKET_ERROR ) {
return -1;
}
ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
/* I connect to the named pipe... */
ret = ConnectNamedPipe(hPipe, &ol);
if( ret == 0 ) {
switch( GetLastError() ) {
case ERROR_PIPE_CONNECTED:
ret = TRUE;
break;
case ERROR_IO_PENDING:
if( WaitForSingleObject(ol.hEvent, PIPE_TIMEOUT_CONNECT) == WAIT_OBJECT_0 ) {
DWORD dwIgnore;
ret = GetOverlappedResult(hPipe, &ol, &dwIgnore, FALSE);
} else {
CancelIo(hPipe);
}
break;
}
}
CloseHandle(ol.hEvent);
if( ret == 0 ) {
return -1;
}
/* I write the socket descriptor to the named pipe */
if ( WriteFile(hPipe, &sock_client, sizeof(sock_client), &dwBytes, NULL) == 0 ) {
return -1;
}
/* I write the protocol information structure to the named pipe */
if ( WriteFile(hPipe, &protInfo, sizeof(protInfo), &dwBytes, NULL) == 0 ) {
return -1;
}
CloseHandle(hPipe);
return 0;
}
Let me explain the code line by line:
- line 13: I create a new pipe through which we’ll pass the duplicate socket;
- line 37: I create a new process by invoking the executable “test.exe”. Among the parameters of the function, we note the “inherit handle = TRUE”, that is the handle will be inheritable;
- line 53: I duplicate the socket through the WSADuplicateSocket() (link). The function parameters are: sock_client (the socket to duplicate), piProc.dwProcessId (process identifier of the target process in which the duplicated socket will be used.) and protInfo(a WSAPROTOCOL_INFO structure that contains the protocol informations);
- line 61 to 101: I have already explained this in the “Timeout on Named Pipes” post (link);
- line 105: I pass through the named pipe the client socket to the child process;
- line 113: I pass through the named pipe the protocol information structure which had been filled before in the WSADuplicateSocket() function.
Ok, now, only for completeness, I’ll show you an example of child process’ source code:
SOCK sock_client;
DWORD dwBytes, dwMode;
HANDLE hPipe;
WSAPROTOCOL_INFO protInfo;
...
while(1){
/* I open in read mode the namedpipe created by the parent process */
hPipe = CreateFile(
PIPE_NAME, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
if (hPipe != INVALID_HANDLE_VALUE) {
break;
}
if ( GetLastError() != ERROR_PIPE_BUSY ) {
ExitProcess(-1);
}
/* if all instances are busy I'll wait 10000 ms */
if ( !WaitNamedPipe(PIPE_NAME, 10000) ) {
ExitProcess(-1);
}
}
/* I set the pipe in read mode */
dwMode = PIPE_READMODE_BYTE;
/* I update the pipe */
SetNamedPipeHandleState(
hPipe,
&dwMode, // new mode
NULL,
NULL);
/* I read the client socket sent me by the parent process */
ReadFile(hPipe, &sock_client, sizeof(sock_client), &dwBytes, NULL);
/* I read the protocol information structure sent me by the parent process */
ReadFile(hPipe, &protInfo, sizeof(protInfo), &dwBytes, NULL);
/* I create a new socket with the structure just read */
sock_client = WSASocket(AF_INET, SOCK_STREAM, 0, &protInfo, 0, WSA_FLAG_OVERLAPPED);
if ( sock_client == INVALID_SOCKET ) {
ExitProcess(-1);
}
// now you can use the socket as you want
...
Let me explain the code line by line:
- line 08 to 42: I open the namedpipe created by the parent process;
- line 44 to 54: I set the namedpipe in read mode;
- line 58: I read from the pipe the client socket descriptor;
- line 62: I read from the pipe the protocol information structure;
- line 66: I create a new socket specifying in the WSASocket() function (link) the procotol information structure just read(protInfo).
I hope this little post will help you to better understand how to create new processes, use the pipe for communication and the passage of the handle between process (in this case socket).
Bye bye.
-
April 20, 2011 at 11:52 | #1win32api passing socket with ipc method[fwd] | wispedia