Thursday, 22 August 2013

Use socket descriptor from another process with Mono

Use socket descriptor from another process with Mono

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.
For Linux, I had a look at the Socket.cs file and see this takes place in
DuplicateAndClose():
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;
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:
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 = &SI.ProtocolInformation[0])
{
uint* source = (uint*)⁡
*((uint*)target) = *source;
}
fixed (byte* target = &SI.ProtocolInformation[4])
{
uint* source = (uint*)&ST;
*((uint*)target) = *source;
}
fixed (byte* target = &SI.ProtocolInformation[8])
{
uint* source = (uint*)&PT;
*((uint*)target) = *source;
}
fixed (byte* target = &SI.ProtocolInformation[12])
{
uint* source = (uint*)&Bound;
*((uint*)target) = *source;
}
fixed (byte* target = &SI.ProtocolInformation[16])
{
long* source = (long*)&Socket;
*((long*)target) = *source;
}
}
So now that I have the SocketInformation populated, I should be able to do
this:
Socket S = new Socket(SI);
S.Send(new byte[] { 65, 66, 67, 68 });
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".
So at first I thought what I wanted to do just wasn't possible, but then I
tried pinvoking a call to send():
Socket S = new Socket(SI);
send(S.Handle, new byte[] { 65, 66, 67, 68 }, 4, SocketFlags.None);
Where send() is declared as:
[DllImport("libc")]
private extern static int send(IntPtr sock, byte[] buf, int count,
SocketFlags flags);
And it works just fine!
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()...

No comments:

Post a Comment