fix ReceiveMessageFromPacketInfo in DualMode ReceiveMessageFromAsync (#39249)
authorTomas Weinfurt <tweinfurt@yahoo.com>
Mon, 3 Aug 2020 17:26:34 +0000 (10:26 -0700)
committerGitHub <noreply@github.com>
Mon, 3 Aug 2020 17:26:34 +0000 (10:26 -0700)
* add test

* fix dual mode on windows

* fix linux

* Update src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs

src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs

index 0c4b0fc..6368666 100644 (file)
@@ -486,13 +486,13 @@ namespace System.Net.Sockets
             bool ipv4 = (_currentSocket!.AddressFamily == AddressFamily.InterNetwork || (ipAddress != null && ipAddress.IsIPv4MappedToIPv6)); // DualMode
             bool ipv6 = _currentSocket.AddressFamily == AddressFamily.InterNetworkV6;
 
-            if (ipv4 && (_controlBufferPinned == null || _controlBufferPinned.Length != sizeof(Interop.Winsock.ControlData)))
+            if (ipv6 && (_controlBufferPinned == null || _controlBufferPinned.Length != sizeof(Interop.Winsock.ControlDataIPv6)))
             {
-                _controlBufferPinned = GC.AllocateUninitializedArray<byte>(sizeof(Interop.Winsock.ControlData), pinned: true);
+                _controlBufferPinned = GC.AllocateUninitializedArray<byte>(sizeof(Interop.Winsock.ControlDataIPv6), pinned: true);
             }
-            else if (ipv6 && (_controlBufferPinned == null || _controlBufferPinned.Length != sizeof(Interop.Winsock.ControlDataIPv6)))
+            else if (ipv4 && (_controlBufferPinned == null || _controlBufferPinned.Length != sizeof(Interop.Winsock.ControlData)))
             {
-                _controlBufferPinned = GC.AllocateUninitializedArray<byte>(sizeof(Interop.Winsock.ControlDataIPv6), pinned: true);
+                _controlBufferPinned = GC.AllocateUninitializedArray<byte>(sizeof(Interop.Winsock.ControlData), pinned: true);
             }
 
             // If single buffer we need a single element WSABuffer.
index d68f47c..1d79475 100644 (file)
@@ -436,6 +436,12 @@ namespace System.Net.Sockets
 
         public static unsafe IPPacketInformation GetIPPacketInformation(Interop.Winsock.ControlDataIPv6* controlBuffer)
         {
+            if (controlBuffer->length == (UIntPtr)sizeof(Interop.Winsock.ControlData))
+            {
+                // IPv4 client connectiong to dual mode socket.
+                return GetIPPacketInformation((Interop.Winsock.ControlData*)controlBuffer);
+            }
+
             IPAddress address = controlBuffer->length != UIntPtr.Zero ?
                 new IPAddress(new ReadOnlySpan<byte>(controlBuffer->address, Interop.Winsock.IPv6AddressLength)) :
                 IPAddress.IPv6None;
@@ -451,7 +457,6 @@ namespace System.Net.Sockets
             bytesTransferred = 0;
             receiveAddress = socketAddress;
             ipPacketInformation = default(IPPacketInformation);
-
             fixed (byte* ptrBuffer = buffer)
             fixed (byte* ptrSocketAddress = socketAddress.Buffer)
             {
index fa81b01..38ea1a2 100644 (file)
@@ -1947,6 +1947,60 @@ namespace System.Net.Sockets.Tests
             ReceiveMessageFrom_Helper(IPAddress.IPv6Any, IPAddress.Loopback);
         }
 
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        [PlatformSpecific(~TestPlatforms.OSX)]  // ReceiveMessageFrom not supported on OSX
+        public void ReceiveMessageFromAsync_SocketAsyncEventArgs_Success(bool ipv4)
+        {
+            const int DataLength = 10;
+            AddressFamily family = ipv4 ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6;
+            IPAddress loopback = ipv4 ? IPAddress.Loopback : IPAddress.Loopback.MapToIPv6();
+            IPAddress clientAddress = ipv4 ? IPAddress.Loopback : IPAddress.IPv6Loopback;
+
+            var completed = new ManualResetEventSlim(false);
+            using (var sender = new Socket(family, SocketType.Dgram, ProtocolType.Udp))
+            using (var receiver = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp))
+            {
+                receiver.DualMode = true;
+                receiver.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.PacketInformation, true);
+                receiver.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.PacketInformation, true);
+                int receiverPort = receiver.BindToAnonymousPort(IPAddress.IPv6Any);
+
+                if (!ipv4)
+                {
+                    sender.DualMode = true;
+                }
+
+                int senderPort = sender.BindToAnonymousPort(loopback);
+                var expectedEP = new IPEndPoint(IPAddress.Loopback.MapToIPv6(), senderPort);
+
+                var args = new SocketAsyncEventArgs() { RemoteEndPoint = new IPEndPoint(IPAddress.IPv6Any, 0) };
+                args.Completed += (s, e) => { Console.WriteLine("Got 1 packet {0} {1}", e.RemoteEndPoint, e.ReceiveMessageFromPacketInfo.Address); completed.Set(); };
+                args.SetBuffer(new byte[DataLength], 0, DataLength);
+
+                var ep = new IPEndPoint(loopback, receiverPort);
+                for (int iters = 0; iters < 5; iters++)
+                {
+                    for (int i = 0; i < TestSettings.UDPRedundancy; i++)
+                    {
+                        sender.SendTo(new byte[DataLength], ep);
+                    }
+
+                    if (!receiver.ReceiveMessageFromAsync(args))
+                    {
+                        completed.Set();
+                    }
+                    Assert.True(completed.Wait(TestSettings.PassingTestTimeout), "Timeout while waiting for connection");
+                    completed.Reset();
+
+                    Assert.Equal(DataLength, args.BytesTransferred);
+                    Assert.Equal(expectedEP, args.RemoteEndPoint);
+                    Assert.True(args.ReceiveMessageFromPacketInfo.Address.Equals(IPAddress.Loopback) || args.ReceiveMessageFromPacketInfo.Address.Equals(IPAddress.Loopback.MapToIPv6()));
+                }
+            }
+        }
+
         private void ReceiveMessageFrom_Helper(IPAddress listenOn, IPAddress connectTo, bool expectedToTimeout = false)
         {
             using (Socket serverSocket = new Socket(SocketType.Dgram, ProtocolType.Udp))