From: Rick Parrish on
> That doesn't make sense.  Socket handles are valid only within the
> owning process.  You have to use WSADuplicateSocket(), directly or
> indirectly, to marshal the socket information to another process for it
> to use the socket.

Using CreateProcess() with bInheritHandles=true allows the child to
use the socket, and I guess that must be what Process.Start() does
when ProcessStartInfo.UseShellExecute=false. I'll have to try using
WSADuplicateSocket() later tonight.

> Note that when using WSADuplicateSocket(), one process can close the
> socket without affecting the other.  They share the same socket
> instance, and the OS keeps track of how many processes are using the
> socket, only actually closing the socket when the last process is done
> with it.

That sounds like exactly what I'd need then.

> Frankly, I'm still not even sure sharing a socket is the best approach.

I'd agree there are better ways, but there are several telnet servers
already available that may not support those other ways. The only one
they all have in common is sharing a socket, so that's what I'm stuck
with.
From: Peter Duniho on
Rick Parrish wrote:
>> That doesn't make sense. Socket handles are valid only within the
>> owning process. You have to use WSADuplicateSocket(), directly or
>> indirectly, to marshal the socket information to another process for it
>> to use the socket.
>
> Using CreateProcess() with bInheritHandles=true allows the child to
> use the socket, and I guess that must be what Process.Start() does
> when ProcessStartInfo.UseShellExecute=false.

Huh? I don't have time to try it now, but�are you saying that _by
default_, doing nothing special, not only is the underlying SOCKET
handle in the Socket class inheritable, but the Process class is
starting the process with bInheritHandles set to TRUE?

That sounds like a very serious security bug to me. Want access to some
inheritable handle in a process that starts other processes? No
problem�just replace a known target child process executable with your
own, and voil�, carte blanche access.

Can you post a concise-but-complete code example demonstrating it?

> [...]
>> Frankly, I'm still not even sure sharing a socket is the best approach.
>
> I'd agree there are better ways, but there are several telnet servers
> already available that may not support those other ways. The only one
> they all have in common is sharing a socket, so that's what I'm stuck
> with.

I guess I don't really get why, just because other telnet servers
implement it this way, you feel yours needs to. The way I suggested is
more general purpose (works with any random console application), while
depending on handle inheritance requires the cooperation of the child
process (you still need to pass the handle value to the child process,
so it knows _which_ handle to use).

But, whatever�if you insist on doing this, I guess you can share handles
if you like. I also don't understand why an inherited handle wouldn't
have the same behavior as a duplicated one; that is, it should have to
be closed in all processes in which it's valid before the OS object is
actually closed. But since it's not a technique I'd use in this way,
I'm not going to worry too much about that. :)

Pete
From: Rick Parrish on
> Huh?  I don't have time to try it now, but…are you saying that _by
> default_, doing nothing special, not only is the underlying SOCKET
> handle in the Socket class inheritable, but the Process class is
> starting the process with bInheritHandles set to TRUE?

Just had a look at Process.cs in the .NET source, and yes, if
UseShellExecute=false then Process.Start() will instead use
CreateProcess(), and in that case, bInheritHandles is hardcoded to
true.

> I guess I don't really get why, just because other telnet servers
> implement it this way, you feel yours needs to.  The way I suggested is
> more general purpose (works with any random console application), while
> depending on handle inheritance requires the cooperation of the child
> process (you still need to pass the handle value to the child process,
> so it knows _which_ handle to use).

It's not that I necessarily want to do things the same way other
telnet servers do it, it's that I have to if I want to maintain
compatibility with them. The 3rd party programs made with this code
could be used under one of many different telnet servers, and while
one does actually support communicating via standard input/output, the
rest only use shared sockets. Passing the handle value to the child
process is standard amongst off the servers, so that's not a problem.

> But, whatever…if you insist on doing this, I guess you can share handles
> if you like.  I also don't understand why an inherited handle wouldn't
> have the same behavior as a duplicated one; that is, it should have to
> be closed in all processes in which it's valid before the OS object is
> actually closed.  But since it's not a technique I'd use in this way,
> I'm not going to worry too much about that.  :)

My guess is that the OS probably doesn't track when inherited handles
are used in multiple processes. It's easy for it to know when
duplicated handles are used, since WSADuplicateSocket() and WSASocket
() have to be used, but with inherited handles I can just pass the ID
of the socket from the parent to the child, and then directly use send
(ID, ...) without doing anything extra.

In the end I've decided just to write my own stripped down Socket.cs
that won't close on dispose. Bit more work than I was hoping for, but
it'll give me the most control.
From: Rick Parrish on
Just for future reference (not that I expect many people will be
interested), I found a much simpler solution that isn't as
questionable as Thread.Sleep(2500) or as much work as creating an
entirely new Socket.cs:

// FSocket is my System.Net.Socket
object m_Handle = typeof(Socket).GetField("m_Handle",
BindingFlags.NonPublic | BindingFlags.Instance).GetValue(FSocket);
Assembly SystemAssembly = Assembly.LoadFile(@"C:\WINDOWS\Microsoft.NET
\Framework\v2.0.50727\System.dll");
SystemAssembly.GetType("System.Net.SafeCloseSocket").GetField
("m_InnerSocket", BindingFlags.NonPublic |
BindingFlags.Instance).SetValue(m_Handle, null);

In Socket.cs, m_Handle is a private field of type SafeCloseSocket,
which in turn has a private field called m_InnerSocket. The above 3
lines effectively set m_InnserSocket to null, so when the cleanup code
occurs, it bypasses the call to closesocket(), leaving our inherited
handle intact when the program terminates.
From: Peter Duniho on
Rick Parrish wrote:
> [...]
> In Socket.cs, m_Handle is a private field of type SafeCloseSocket,
> which in turn has a private field called m_InnerSocket. The above 3
> lines effectively set m_InnserSocket to null, so when the cleanup code
> occurs, it bypasses the call to closesocket(), leaving our inherited
> handle intact when the program terminates.

Quite frankly, that is an _awful_ solution. It is entirely
implementation dependent, and likely to fail to work in some future
version of .NET. It definitely is "as questionable as" using
Thread.Sleep().

It's bad enough you find yourself needing to share socket handles
between processes. Don't compound the problem by using reflection to
get at implementation dependent particulars that could change at any time.

If you insist on relying on a handle-sharing implementation for your own
code, at least do it in a reliable way, such as providing your own
socket implementation that allows you to leave a socket unclosed.

Of course, even doing that you have failed to take into account the fact
that the _unmanaged_ object is still effectively managed by the OS, and
will still be closed at some point after your process exits. But if for
some reason not closing it explicitly when your process exits lets it
live long enough for your purposes, then at least achieve that goal in a
reliable way.

Pete