Skip to content

Commit d56934f

Browse files
Update sockets to support IPV6 (#309)
* Update sockets to support IPV6 * Bump assembly native version Co-authored-by: José Simões <[email protected]>
1 parent f160b12 commit d56934f

File tree

8 files changed

+298
-87
lines changed

8 files changed

+298
-87
lines changed

nanoFramework.System.Net/DNS.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,15 @@ public static IPHostEntry GetHostEntry(string hostNameOrAddress)
4141
if (family == AddressFamily.InterNetwork)
4242
{
4343
uint ipAddr = (uint)((address[7] << 24) | (address[6] << 16) | (address[5] << 8) | (address[4]));
44-
4544
ipAddresses[i] = new IPAddress(ipAddr);
4645
}
46+
else if (family == AddressFamily.InterNetworkV6)
47+
{
48+
byte[] ipv6addr = new byte[16];
49+
Array.Copy(address, 4, ipv6addr, 0, 16);
50+
51+
ipAddresses[i] = new IPAddress(ipv6addr);
52+
}
4753
else
4854
{
4955
throw new NotImplementedException();

nanoFramework.System.Net/IPAddress.cs

Lines changed: 224 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ namespace System.Net
1616
[Serializable]
1717
public class IPAddress
1818
{
19+
internal const int IPv4AddressBytes = 4;
20+
internal const int IPv6AddressBytes = 16;
21+
22+
internal const int NumberOfLabels = IPv6AddressBytes / 2;
23+
1924
/// <summary>
2025
/// Provides an IP address that indicates that the server must listen for client activity on all network interfaces. This field is read-only.
2126
/// </summary>
@@ -28,16 +33,25 @@ public class IPAddress
2833

2934
internal readonly long Address;
3035

36+
/// <summary>
37+
/// The Bind(EndPoint) method uses the IPv6Any field to indicate that a Socket must listen for client activity on all network interfaces.
38+
/// </summary>
39+
public static readonly IPAddress IPv6Any = new IPAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0);
40+
41+
/// <summary>
42+
/// Provides the IP loopback address. This property is read-only.
43+
/// </summary>
44+
public static readonly IPAddress IPv6Loopback = new IPAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 0);
45+
46+
3147
[Diagnostics.DebuggerBrowsable(Diagnostics.DebuggerBrowsableState.Never)]
3248
private readonly AddressFamily _family = AddressFamily.InterNetwork;
3349

3450
[Diagnostics.DebuggerBrowsable(Diagnostics.DebuggerBrowsableState.Never)]
3551
private readonly ushort[] _numbers = new ushort[NumberOfLabels];
3652

37-
internal const int IPv4AddressBytes = 4;
38-
internal const int IPv6AddressBytes = 16;
39-
40-
internal const int NumberOfLabels = IPv6AddressBytes / 2;
53+
[Diagnostics.DebuggerBrowsable(Diagnostics.DebuggerBrowsableState.Never)]
54+
private long _scopeid = 0;
4155

4256
/// <summary>
4357
/// Gets the address family of the IP address.
@@ -81,7 +95,7 @@ public IPAddress(long newAddress)
8195
/// <summary>
8296
/// Initializes a new instance of the <see cref="IPAddress"/> class with the address specified as a Byte array.
8397
/// </summary>
84-
/// <param name="address"></param>
98+
/// <param name="address">The byte array value of the IP address.</param>
8599
/// <exception cref="ArgumentNullException"><paramref name="address"/> is <see langword="null"/>.</exception>
86100
/// <exception cref="ArgumentException"><paramref name="address"/> contains a bad IP address.</exception>
87101
/// <remarks>
@@ -122,6 +136,40 @@ public IPAddress(byte[] address)
122136
}
123137
}
124138

139+
/// <summary>
140+
/// Initializes a new instance of a IPV6 <see cref="IPAddress"/> class with the address specified as a Byte array.
141+
/// </summary>
142+
/// <param name="address">The byte array value of the IP address.</param>
143+
/// <param name="scopeid">The long value of the scope identifier.</param>
144+
/// <exception cref="ArgumentNullException"><paramref name="address"/> is <see langword="null"/>.</exception>
145+
/// <exception cref="ArgumentException"><paramref name="address"/> contains a bad IP address.</exception>
146+
/// <remarks>
147+
/// The IPAddress is created with the <see cref="Address"/> property set to <paramref name="address"/>.
148+
/// If the length of <paramref name="address"/> is 4, <see cref="IPAddress"/>(Byte[]) constructs an IPv4 address; otherwise, an IPv6 address with a scope of 0 is constructed.
149+
/// The <see cref="Byte"/> array is assumed to be in network byte order with the most significant byte first in index position 0.
150+
/// </remarks>
151+
public IPAddress(byte[] address, long scopeid) : this(address)
152+
{
153+
if (address.Length == IPv6AddressBytes)
154+
{
155+
_scopeid = scopeid;
156+
}
157+
else
158+
{
159+
// Not IPV6 address
160+
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
161+
throw new ArgumentException();
162+
#pragma warning restore S3928 // OK to throw this here
163+
}
164+
}
165+
166+
private IPAddress(ushort[] address, uint scopeid)
167+
{
168+
_family = AddressFamily.InterNetworkV6;
169+
_numbers = address;
170+
_scopeid = scopeid;
171+
}
172+
125173
/// <summary>
126174
/// Compares two IP addresses.
127175
/// </summary>
@@ -133,7 +181,31 @@ public override bool Equals(object obj)
133181

134182
if (obj == null) return false;
135183

136-
return this.Address == addr.Address;
184+
// Compare family before address
185+
if (_family != addr.AddressFamily)
186+
{
187+
return false;
188+
}
189+
190+
if (_family == AddressFamily.InterNetworkV6)
191+
{
192+
// For IPv6 addresses, compare the full 128bit address
193+
for (int i = 0; i < NumberOfLabels; i++)
194+
{
195+
if (addr._numbers[i] != this._numbers[i])
196+
return false;
197+
}
198+
199+
// Also scope must match
200+
if (addr._scopeid == this._scopeid)
201+
return true;
202+
203+
return false;
204+
}
205+
else
206+
{
207+
return this.Address == addr.Address;
208+
}
137209
}
138210

139211
/// <summary>
@@ -142,13 +214,30 @@ public override bool Equals(object obj)
142214
/// <returns>A Byte array.</returns>
143215
public byte[] GetAddressBytes()
144216
{
145-
return new byte[]
217+
byte[] bytes;
218+
219+
if (_family == AddressFamily.InterNetworkV6)
146220
{
147-
(byte)(Address),
148-
(byte)(Address >> 8),
149-
(byte)(Address >> 16),
150-
(byte)(Address >> 24)
151-
};
221+
bytes = new byte[NumberOfLabels * 2];
222+
223+
int j = 0;
224+
for (int i = 0; i < NumberOfLabels; i++)
225+
{
226+
bytes[j++] = (byte)((this._numbers[i] >> 8) & 0xFF);
227+
bytes[j++] = (byte)((this._numbers[i]) & 0xFF);
228+
}
229+
return bytes;
230+
}
231+
else
232+
{
233+
return new byte[]
234+
{
235+
(byte)(Address),
236+
(byte)(Address >> 8),
237+
(byte)(Address >> 16),
238+
(byte)(Address >> 24)
239+
};
240+
}
152241
}
153242

154243
/// <summary>
@@ -159,7 +248,40 @@ public byte[] GetAddressBytes()
159248
/// </returns>
160249
public static IPAddress Parse(string ipString)
161250
{
162-
return new IPAddress(NetworkInterface.IPAddressFromString(ipString));
251+
// Check for IPV6 string and use separate parse method
252+
if (ipString.IndexOf(':') != -1)
253+
{
254+
return new IPAddress(NetworkInterface.IPV6AddressFromString(ipString), 0);
255+
}
256+
257+
return new IPAddress(NetworkInterface.IPV4AddressFromString(ipString));
258+
}
259+
260+
/// <summary>
261+
/// Get or Set IPV6 scope identifier.
262+
/// </summary>
263+
public long ScopeId
264+
{
265+
get
266+
{
267+
// Not valid for IPv4 addresses
268+
if (_family == AddressFamily.InterNetwork)
269+
{
270+
throw new SocketException(SocketError.OperationNotSupported);
271+
}
272+
273+
return _scopeid;
274+
}
275+
set
276+
{
277+
// Not valid for IPv4 addresses
278+
if (_family == AddressFamily.InterNetwork)
279+
{
280+
throw new SocketException(SocketError.OperationNotSupported);
281+
}
282+
283+
_scopeid = value;
284+
}
163285
}
164286

165287
/// <summary>
@@ -172,6 +294,11 @@ public static IPAddress Parse(string ipString)
172294
/// </remarks>
173295
public override string ToString()
174296
{
297+
if (_family == AddressFamily.InterNetworkV6)
298+
{
299+
return IPv6ToString(_numbers);
300+
}
301+
175302
return IPv4ToString((uint)Address);
176303
}
177304

@@ -229,18 +356,100 @@ internal IPAddress Snapshot()
229356
case AddressFamily.InterNetwork:
230357
return new IPAddress(Address);
231358

232-
//case AddressFamily.InterNetworkV6:
233-
// return new IPAddress(m_Numbers, (uint)m_ScopeId);
359+
case AddressFamily.InterNetworkV6:
360+
return new IPAddress(_numbers, (uint)_scopeid);
234361
}
235362

236363
throw new NotSupportedException();
237364
}
238365

366+
/// <summary>
367+
/// Gets whether the IP address is an IPv4-mapped IPv6 address.
368+
/// </summary>
369+
/// <returns>
370+
/// true if the IP address is an IPv4-mapped IPv6 address; otherwise, false.
371+
/// </returns>
372+
public bool IsIPv4MappedToIPv6
373+
{
374+
get
375+
{
376+
if (AddressFamily != AddressFamily.InterNetworkV6)
377+
{
378+
return false;
379+
}
380+
381+
for (int i = 0; i < 5; i++)
382+
{
383+
if (_numbers[i] != 0)
384+
{
385+
return false;
386+
}
387+
}
388+
389+
return (_numbers[5] == 0xFFFF);
390+
}
391+
}
392+
393+
/// <summary>
394+
/// Maps the <see cref="IPAddress"/> object to an IPv6 address.
395+
/// </summary>
396+
/// <remarks>
397+
/// Dual-stack sockets always require IPv6 addresses. The ability to interact with an IPv4 address
398+
/// requires the use of the IPv4-mapped IPv6 address format. Any IPv4 addresses must be represented
399+
/// in the IPv4-mapped IPv6 address format which enables an IPv6 only application to communicate
400+
/// with an IPv4 node.
401+
/// For example. IPv4 192.168.1.4 maps as IPV6 ::FFFF:192.168.1.4
402+
/// </remarks>
403+
/// <returns>
404+
/// Returns <see cref="IPAddress"/>. An IPV6 address.
405+
/// </returns>
406+
public IPAddress MapToIPv6()
407+
{
408+
if (AddressFamily == AddressFamily.InterNetworkV6)
409+
{
410+
return this;
411+
}
412+
413+
ushort[] labels = new ushort[IPAddress.NumberOfLabels];
414+
labels[5] = 0xFFFF;
415+
labels[6] = (ushort)(((Address & 0x0000FF00) >> 8) | ((Address & 0x000000FF) << 8));
416+
labels[7] = (ushort)(((Address & 0xFF000000) >> 24) | ((Address & 0x00FF0000) >> 8));
417+
418+
return new IPAddress(labels, 0);
419+
}
420+
421+
/// <summary>
422+
/// Maps the <see cref="IPAddress"/> object to an IPv4 address.
423+
/// </summary>
424+
/// <remarks>
425+
/// Dual-stack sockets always require IPv6 addresses. The ability to interact with an IPv4
426+
/// address requires the use of the IPv4-mapped IPv6 address format.
427+
/// </remarks>
428+
/// <returns>
429+
/// Returns <see cref="IPAddress"/>. An IPV4 address.
430+
/// </returns>
431+
public IPAddress MapToIPv4()
432+
{
433+
if (AddressFamily == AddressFamily.InterNetwork)
434+
{
435+
return this;
436+
}
437+
438+
long address = ((((uint)_numbers[6] & 0x0000FF00u) >> 8) |
439+
(((uint)_numbers[6] & 0x000000FFu) << 8)) |
440+
(((((uint)_numbers[7] & 0x0000FF00u) >> 8) |
441+
(((uint)_numbers[7] & 0x000000FFu) << 8)) << 16);
442+
443+
return new IPAddress(address);
444+
}
445+
239446
#region native methods
240447

241448
[MethodImpl(MethodImplOptions.InternalCall)]
242449
internal static extern string IPv4ToString(uint ipv4Address);
243450

451+
[MethodImpl(MethodImplOptions.InternalCall)]
452+
internal static extern string IPv6ToString(ushort[] ipv6Address);
244453
#endregion
245454
}
246455
}

0 commit comments

Comments
 (0)