[Mono-bugs] [Bug 349449] New: LocalEndPoint and RemoteEndPointer properties are null for socket connected in non-blocking mode
bugzilla_noreply at novell.com
bugzilla_noreply at novell.com
Tue Dec 18 02:20:11 EST 2007
https://bugzilla.novell.com/show_bug.cgi?id=349449
Summary: LocalEndPoint and RemoteEndPointer properties are null
for socket connected in non-blocking mode
Product: Mono: Class Libraries
Version: 1.2.6
Platform: 64bit
OS/Version: Ubuntu
Status: NEW
Severity: Normal
Priority: P5 - None
Component: System
AssignedTo: mono-bugs at ximian.com
ReportedBy: michi at zeroc.com
QAContact: mono-bugs at ximian.com
Found By: Customer
Below is an example program that fails under Mono 1.2.6 but works with earlier
versions. The problem is that, for a socket that is connected non-blocking,
IPEndpoint.LocalEndPoint and IPEndPoint.RemoteEndpoint return null.
It can be argued that this change is actually a bug fix rather than a bug
because Windows does the same thing for sockets that are connected non-blocking
(even though that seems non-sensical).
So, prior to 1.2.6, my solution was to, for Windows, use P/Invoke into
wsock32.dll to call getsockname() and getpeername(). The same thing works for
Mono 1.2.6. However, the added difficulty is that the library name is
platform-dependent, which makes it difficult for us to ship code that will work
for differnet Linunx distributions.
I would very much appreciate any suggestions you might have as to how I can get
the local and remote endpoint from a socket that was connected non-blocking
without having to use P/Invoke. (I've attached a section of code that
illustrates my dilemma following the test case.)
The code below fails on Mono 1.2.6, but works on 1.2.5 and earlier version. To
test, run a server on 127.0.0.1 that accepts connections on port 12010.
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
class Test
{
public static void Main(string[] args)
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1);
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive,
1);
s.Blocking = false;
IPEndPoint e = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12010);
try
{
s.Connect(e);
}
catch(SocketException ex)
{
if(ex.NativeErrorCode != 10035) // WSAEWOULDBLOCK
{
System.Console.Error.WriteLine("Non-blocking connect failed");
Environment.Exit(1);
}
}
List<Socket> rl = new List<Socket>();
rl.Add(s);
List<Socket> wl = new List<Socket>();
wl.Add(s);
List<Socket> el = new List<Socket>();
el.Add(s);
Socket.Select(rl, wl, el, 10000000);
if(el.Count != 0)
{
System.Console.Error.WriteLine("Select error");
}
if((int)s.GetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.Error) > 0)
{
System.Console.Error.WriteLine("Connect failed");
Environment.Exit(1);
}
IPEndPoint localEndpoint = (IPEndPoint)s.LocalEndPoint;
if(localEndpoint == null)
{
System.Console.Error.WriteLine("local endpoint of connected socket
is null?!!!");
}
IPEndPoint remoteEndpoint = (IPEndPoint)s.RemoteEndPoint;
if(remoteEndpoint == null)
{
System.Console.Error.WriteLine("remote endpoint of connected socket
is null?!!!");
}
if(localEndpoint == null || remoteEndpoint == null)
{
Environment.Exit(1);
}
Environment.Exit(0);
}
}
Here is how I'm working around the problem at the moment, but I would really
like to have something that avoids having to use P/Invoke:
[StructLayout(LayoutKind.Sequential)]
private struct in_addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public byte[] sin_addr;
}
[StructLayout(LayoutKind.Sequential)]
private struct sockaddr
{
public short sin_family;
public ushort sin_port;
public in_addr sin_addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
public byte[] sin_zero;
}
// The problem is the DllImport("libc-2.3.6.so") because that's
// platform-dependent.
// ...
#if __MonoCS__
[DllImport("libc-2.3.6.so")]
#else
[DllImport("wsock32.dll")]
#endif
private static extern int getpeername(IntPtr s, ref sockaddr name, ref
int namelen);
// More definitions here like the previous one for getsockname(), inet_ntoa(),
and ntohs().
// ...
// The code below uses P/Invoke to call getpeername() to get the
// remote endpoint. As such, that's OK, but the problem
// is that the DllImport above is not portable.
public static IPEndPoint
getRemoteAddress(Socket socket)
{
//
// .Net BUG: The LocalEndPoint and RemoteEndPoint properties
// are null for a socket that was connected in non-blocking
// mode. As of Mono 1.2.6, Mono behaves the same way.
// The only way to make this work is to step down to
// the native API and use platform invoke :-(
//
IPEndPoint remoteEndpoint = null;
sockaddr addr = new sockaddr();
int addrLen = 16;
if(getpeername(socket.Handle, ref addr, ref addrLen) == 0)
{
string ip = Marshal.PtrToStringAnsi(inet_ntoa(addr.sin_addr));
int port = ntohs(addr.sin_port);
remoteEndpoint = new IPEndPoint(IPAddress.Parse(ip), port);
}
return remoteEndpoint;
}
--
Configure bugmail: https://bugzilla.novell.com/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the QA contact for the bug.
You are the assignee for the bug.
More information about the mono-bugs
mailing list