[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