Skip to content

Commit 4c86e8f

Browse files
authored
use dual mode sockets in cases when address family is not set explicitly (dotnet#30036)
* use dual mode sockets in cases when address family is not set explicitly * update test so it does not fail on platforms without DualMode. In that case it will simply verify that TcpClient without explicit AF can be created without exception.
1 parent af24802 commit 4c86e8f

File tree

2 files changed

+51
-6
lines changed

2 files changed

+51
-6
lines changed

src/System.Net.Sockets/src/System/Net/Sockets/TCPClient.cs

+24-5
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class TcpClient : IDisposable
2020
private bool _active;
2121

2222
// Initializes a new instance of the System.Net.Sockets.TcpClient class.
23-
public TcpClient() : this(AddressFamily.InterNetwork)
23+
public TcpClient() : this(AddressFamily.Unknown)
2424
{
2525
}
2626

@@ -31,7 +31,8 @@ public TcpClient(AddressFamily family)
3131

3232
// Validate parameter
3333
if (family != AddressFamily.InterNetwork &&
34-
family != AddressFamily.InterNetworkV6)
34+
family != AddressFamily.InterNetworkV6 &&
35+
family != AddressFamily.Unknown)
3536
{
3637
throw new ArgumentException(SR.Format(SR.net_protocol_invalid_family, "TCP"), nameof(family));
3738
}
@@ -111,7 +112,11 @@ protected bool Active
111112
public Socket Client
112113
{
113114
get { return _clientSocket; }
114-
set { _clientSocket = value; }
115+
set
116+
{
117+
_clientSocket = value;
118+
_family = _clientSocket?.AddressFamily ?? AddressFamily.Unknown;
119+
}
115120
}
116121

117122
public bool Connected => _clientSocket?.Connected ?? false;
@@ -186,7 +191,7 @@ public void Connect(string hostname, int port)
186191
_active = true;
187192
break;
188193
}
189-
else if (address.AddressFamily == _family)
194+
else if (address.AddressFamily == _family || _family == AddressFamily.Unknown)
190195
{
191196
// Only use addresses with a matching family
192197
Connect(new IPEndPoint(address, port));
@@ -257,6 +262,7 @@ public void Connect(IPEndPoint remoteEP)
257262
}
258263

259264
Client.Connect(remoteEP);
265+
_family = Client.AddressFamily;
260266
_active = true;
261267

262268
if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
@@ -267,6 +273,7 @@ public void Connect(IPAddress[] ipAddresses, int port)
267273
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, ipAddresses);
268274

269275
Client.Connect(ipAddresses, port);
276+
_family = Client.AddressFamily;
270277
_active = true;
271278

272279
if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
@@ -468,7 +475,19 @@ public bool NoDelay
468475
private void InitializeClientSocket()
469476
{
470477
Debug.Assert(_clientSocket == null);
471-
_clientSocket = new Socket(_family, SocketType.Stream, ProtocolType.Tcp);
478+
if (_family == AddressFamily.Unknown)
479+
{
480+
// If AF was not explicitly set try to initialize dual mode socket or fall-back to IPv4.
481+
_clientSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
482+
if (_clientSocket.AddressFamily == AddressFamily.InterNetwork)
483+
{
484+
_family = AddressFamily.InterNetwork;
485+
}
486+
}
487+
else
488+
{
489+
_clientSocket = new Socket(_family, SocketType.Stream, ProtocolType.Tcp);
490+
}
472491
}
473492
}
474493
}

src/System.Net.Sockets/tests/FunctionalTests/TcpClientTest.cs

+27-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ public TcpClientTest(ITestOutputHelper output)
2525
[InlineData(AddressFamily.DataLink)]
2626
[InlineData(AddressFamily.NetBios)]
2727
[InlineData(AddressFamily.Unix)]
28-
[InlineData(AddressFamily.Unknown)]
2928
public void Ctor_InvalidFamily_Throws(AddressFamily family)
3029
{
3130
AssertExtensions.Throws<ArgumentException>("family", () => new TcpClient(family));
@@ -430,6 +429,33 @@ public async Task Dispose_CancelsConnectAsync(bool connectByName)
430429
}
431430
}
432431

432+
[Fact]
433+
public void Connect_Dual_Success()
434+
{
435+
if (!Socket.OSSupportsIPv6)
436+
{
437+
return;
438+
}
439+
440+
using (var server = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp))
441+
{
442+
// Set up a server socket to which to connect
443+
server.Bind(new IPEndPoint(IPAddress.IPv6Loopback, 0));
444+
server.Listen(1);
445+
var endpoint = (IPEndPoint)server.LocalEndPoint;
446+
447+
using (TcpClient client = new TcpClient())
448+
{
449+
// Some platforms may not support IPv6 dual mode and they should fall-back to IPv4
450+
// without throwing exception. However in such case attempt to connect to IPv6 would still fail.
451+
if (client.Client.AddressFamily == AddressFamily.InterNetworkV6 && client.Client.DualMode)
452+
{
453+
client.Connect(endpoint);
454+
}
455+
}
456+
}
457+
}
458+
433459
private sealed class DerivedTcpClient : TcpClient
434460
{
435461
public new bool Active

0 commit comments

Comments
 (0)