title: Use socket descriptor from another process with Mono
body: <p>I have a BBS door written in C# that I'd like to get working on Linux.  The Socket class of the .NET framework doesn't support opening existing socket handles, so different work-arounds need to be implemented for both Windows and Linux.</p>
  
  <p>For Linux, I had a look at the Socket.cs file and see this takes place in DuplicateAndClose():</p>
  
  <pre><code>var si = new SocketInformation ();
  si.Options =
      (islistening ? SocketInformationOptions.Listening : 0) |
      (connected ? SocketInformationOptions.Connected : 0) |
      (blocking ? 0 : SocketInformationOptions.NonBlocking) |
      (useoverlappedIO ? SocketInformationOptions.UseOnlyOverlappedIO : 0);
  
  si.ProtocolInformation = Mono.DataConverter.Pack ("iiiil", 
      (int)address_family, 
      (int)socket_type, 
      (int)protocol_type, 
      isbound ? 1 : 0, 
      (long)socket);
  
  socket = (IntPtr) (-1);
  
  return si;
  </code></pre>
  
  <p>Since I don't have access to Mono.DataConverter from Windows, I had a look at the source for it as well and came up with this:</p>
  
  <pre><code>SocketInformation SI = new SocketInformation();
  SI.Options = SocketInformationOptions.Connected;
  SI.ProtocolInformation = new byte[24];
  
  Int32 AF = (Int32)AddressFamily.InterNetwork;
  Int32 ST = (Int32)SocketType.Stream;
  Int32 PT = (Int32)ProtocolType.Tcp;
  Int32 Bound = 0;
  Int64 Socket = (Int64)ASocketHandle;
  unsafe
  {
      fixed (byte* target = &amp;SI.ProtocolInformation[0])
      {
          uint* source = (uint*)&amp;AF;
          *((uint*)target) = *source;
      }
      fixed (byte* target = &amp;SI.ProtocolInformation[4])
      {
          uint* source = (uint*)&amp;ST;
          *((uint*)target) = *source;
      }
      fixed (byte* target = &amp;SI.ProtocolInformation[8])
      {
          uint* source = (uint*)&amp;PT;
          *((uint*)target) = *source;
      }
      fixed (byte* target = &amp;SI.ProtocolInformation[12])
      {
          uint* source = (uint*)&amp;Bound;
          *((uint*)target) = *source;
      }
      fixed (byte* target = &amp;SI.ProtocolInformation[16])
      {
          long* source = (long*)&amp;Socket;
          *((long*)target) = *source;
      }
  }
  </code></pre>
  
  <p>So now that I have the SocketInformation populated, I should be able to do this:</p>
  
  <pre><code>Socket S = new Socket(SI);
  S.Send(new byte[] { 65, 66, 67, 68 });
  </code></pre>
  
  <p>And the remote user should see ABCD.  But instead the call to Send throws an exception with the message "The descriptor is not a socket".</p>
  
  <p>So at first I thought what I wanted to do just wasn't possible, but then I tried pinvoking a call to send():</p>
  
  <pre><code>Socket S = new Socket(SI);
  send(S.Handle, new byte[] { 65, 66, 67, 68 }, 4, SocketFlags.None);
  </code></pre>
  
  <p>Where send() is declared as:</p>
  
  <pre><code>[DllImport("libc")]
  private extern static int send(IntPtr sock, byte[] buf, int count, SocketFlags flags);
  </code></pre>
  
  <p>And it works just fine!  </p>
  
  <p>So if a pinvoked call to send() is fine with the descriptor, what am I doing wrong to have the managed S.Send() call fail saying it's not a socket descriptor?  I'm assuming it has something to do with how I'm populating SocketInformation, but if nothing else the handle seems to be populated correctly since I'm able to use it with the pinvoked send()...</p>
  
  <p>EDIT: After further digging into the Mono source, I may have tracked down the problem:</p>
  
  <p>S.Send() will call Send_internal(), which will in turn call _wapi_send().  This is where the call to send() is ultimately made, but before that this happens:</p>
  
  <pre><code>if (_wapi_handle_type (handle) != WAPI_HANDLE_SOCKET) {
      WSASetLastError (WSAENOTSOCK);
      return(SOCKET_ERROR);
  }
  </code></pre>
  
  <p>So I'm guessing _wapi_handle_type doesn't return WAPI_HANDLE_SOCKET (probably because the handle was passed in and not created by the process, so it doesn't know the handle type), and so that's where my mysterious "The descriptor is not a socket" error is coming from.</p>
  
  <p>Guess this is a long shot, but has anybody else run into this problem?  Is there a solution, aside from going fully unmanaged with the socket i/o?</p>