Home > C/C++, Windows > Win32 API: Passing Socket with IPC method

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.

About these ads
  1. Giacomo
    September 23, 2014 at 20:19

    I always get a “Too many open sockets (10024)” error calling WSADuplicateSocket.
    Is there a solution for that issue? I can’t find a solution :(

  1. April 20, 2011 at 11:52

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: