Socket Duplication - Part 2

Is it possible to transfer sockets from unmanaged processes?

So, a question now arises.. Is it possible to transfer sockets from unmanaged processes to managed processes? The answer is yes.  Isn't that cool? There are a lot of applications around that have a set of managed processes and unmanaged processes as a part of their application. They are mainly managed processes, using unmanaged code for the parts that are time-consuming - just to increase the speedup.

 

So what has to be done to achieve this. Get the WSAProtocolInfo from the WSADuplicateSocket call

WSADuplicateSocketW(m_socket, pid, lpWSAProtocolInfo)

and pass it on to the managed process.

In the managed process create a SocketInformation object.

        SocketInformation sockInfo = new SocketInformation();

The WSAProtocolInfo bytes become the ProtocolInformation field of the SocketInformation object.

        byte[] protocolInfo;

// receive protocolInfo from some Interprocess communication mechanism

        sockInfo.ProtocolInformation = protocolInfo;

The managed code keeps track of the state of the socket separately. Though some of the state information can be queried from the socket itself, it is not possible to get all the required information. So you have to explicitly set the state in the Socket in the SocketInformation class-> whether the socket is a NonBlocking socket, if it was connected, if it was listening or if UseOnlyOverlappedIO option was set in the socket at the time of duplication. This is through the Options field in SocketInformation object.

        sockInfo.Options = SocketInformationOptions.Listening | SocketInformationOptions.Connected;

 

 

Here is an example on how to transfer sockets back and forth managed and unmanaged processes. The example shows a socket, which binds and listens in the unmanaged process, and then accepts connections in the managed process.

 

Example in detail … The unmanaged process creates the socket that needs to be duplicated, binds and listens on that socket. It then duplicates and transfers the socket to the managed process using another socket. The managed process receives the duplicated structure and creates a socket out of the socket information. Once it creates a socket, it tests if the new socket can accept and send and receive data by connecting a test socket to the duplicated socket..

 

 

 

 

Unmanaged code:

#include <stdio.h>

#include <winsock2.h>

#include <process.h>

void main(int argc, char* argv[]) {

    // Initialize Winsock.

    WSADATA wsaData;

    LPWSAPROTOCOL_INFOW lpWSAProtocolInfo;

    int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );

    if ( iResult != NO_ERROR )

        printf("Error at WSAStartup()\n");

    // Create a socket that is to be duplicated.

  SOCKET m_socket;

    m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

    if ( m_socket == INVALID_SOCKET ) {

        printf( "Error at socket(): %ld\n", WSAGetLastError() );

        WSACleanup();

        return;

    }

    printf("Created the socket that has to be duplicated\n");

// bind this to a service port and make the socket listen for client connections

    sockaddr_in clientService;

    clientService.sin_family = AF_INET;

    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");

   clientService.sin_port = htons( 2000 );

    if(bind(m_socket, (struct sockaddr*)&clientService, sizeof(clientService))<0)

    {

        printf( "error at bind socket(): %ld\n", WSAGetLastError() );

        WSACleanup();

        return;

    }

      if(listen(m_socket, 5) == SOCKET_ERROR)

      {

        printf( "error at listen socket(): %ld\n", WSAGetLastError() );

        WSACleanup();

        return;

      }

      printf("Bind and listen on that socket %s\n", argv[1]);

     

    int pid = atoi(argv[1]);

    printf("duplicating for process pid %d\n", pid);

    lpWSAProtocolInfo = (WSAPROTOCOL_INFOW*) malloc(sizeof(WSAPROTOCOL_INFOW));

    if(WSADuplicateSocketW(m_socket, pid, lpWSAProtocolInfo) == SOCKET_ERROR)

      {

        printf( "error while Duplicating %ld\n", WSAGetLastError());

        WSACleanup();

        return;

      }

      printf("Duplicated the socekt\n");

    printf("Now create another socket to transfer this protocolinfo to the managed end\n");

      SOCKET transportSocket;

    transportSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

    clientService.sin_family = AF_INET;

    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");

    clientService.sin_port = htons( 2001 );

    if(connect(transportSocket, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR)

    {

        printf( "error at connect socket(): %ld\n", WSAGetLastError() );

        WSACleanup();

        return;

    }

    printf("connected the transport socket--> now transferring protocolinfo of size %d\n" , sizeof(WSAPROTOCOL_INFOW));

  

    send(transportSocket, (char*) lpWSAProtocolInfo, sizeof(WSAPROTOCOL_INFOW), 0);

      printf("sent protocolinfo data thru socket\n");

      closesocket(transportSocket);

      closesocket(m_socket);

    WSACleanup();

    return;

}

 

 

Managed:

using System;

using System.Net;

using System.Net.Sockets;

using System.Threading;

using System.Diagnostics;

public class ManagedDuplication

{

    public static void Main()

    {

// Run the unmanaged process -which creates a socket, duplicates it and transfers the struct thru another socket – you can start the unmanaged.exe separately in another command prompt too..

        Process p = new Process();

        string command = "Unmanaged.exe";

  p = Process.Start(command, Process.GetCurrentProcess().Id.ToString());

        //create a socket to receive the duplicated socket

        Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        serverSocket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2001));

    serverSocket.Listen(5);

        Socket clientSocket = serverSocket.Accept();

        //receive protocolinfo of the duplicated socket

        byte[] recvBytes = new byte[10000];

        Console.WriteLine("Waiting to receive protocolinfo......");

        int count = clientSocket.Receive(recvBytes);

        Console.WriteLine("Received " + count);

        //Create the socketInformation out of the received Bytes.

        //specify the state of the socket

        //The socket had already started to listen in unmanaged code

      

        SocketInformation sockInfo = new SocketInformation();

        byte[] protocolInfo = new byte[count];

        Array.Copy(recvBytes, protocolInfo, count);

        sockInfo.ProtocolInformation = protocolInfo;

        sockInfo.Options = SocketInformationOptions.Listening;

        clientSocket.Close();

        //Create the socket from protocolInfo

        Console.WriteLine("Trying to create socket out of protocolinfo");

        Socket duplicatedSocket = new Socket(sockInfo);

        Console.WriteLine("Successfully created socket -Now trying to accept");

        //create a socket, connect to the duplicated socket to see if the duplicated

        //socket is able to receive connections and send and receive data

        Socket testSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        testSocket.BeginConnect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2000), new AsyncCallback(ConnectCallback), testSocket);

        clientSocket = duplicatedSocket.Accept();

        //send data to the testsocket

        clientSocket.Send(new byte[100]);

        byte[] b = new byte[100];

        count = clientSocket.Receive(b);

        Console.WriteLine("received {0} bytes", count);

        //if we are here then the duplicated socket has received data - > test has succeeded

        clientSocket.Close();

        duplicatedSocket.Close();

        testSocket.Close();

        return;

    }

 

    public static void ConnectCallback(IAsyncResult result)

    {

        Socket socket = (Socket)result.AsyncState;

        Console.WriteLine("Test socket trying to connect to the duplicated socket");

        socket.EndConnect(result);

        byte[] b = new byte[1000];

        int count = socket.Receive(b);

        socket.Send(new byte[count]);

        socket.Close();

    }

}