diff --git a/nanoFramework.System.Net/DNS.cs b/nanoFramework.System.Net/DNS.cs index 0941650..ded6b36 100644 --- a/nanoFramework.System.Net/DNS.cs +++ b/nanoFramework.System.Net/DNS.cs @@ -41,9 +41,15 @@ public static IPHostEntry GetHostEntry(string hostNameOrAddress) if (family == AddressFamily.InterNetwork) { uint ipAddr = (uint)((address[7] << 24) | (address[6] << 16) | (address[5] << 8) | (address[4])); - ipAddresses[i] = new IPAddress(ipAddr); } + else if (family == AddressFamily.InterNetworkV6) + { + byte[] ipv6addr = new byte[16]; + Array.Copy(address, 4, ipv6addr, 0, 16); + + ipAddresses[i] = new IPAddress(ipv6addr); + } else { throw new NotImplementedException(); diff --git a/nanoFramework.System.Net/IPAddress.cs b/nanoFramework.System.Net/IPAddress.cs index ff3b20f..ed091f8 100644 --- a/nanoFramework.System.Net/IPAddress.cs +++ b/nanoFramework.System.Net/IPAddress.cs @@ -16,6 +16,11 @@ namespace System.Net [Serializable] public class IPAddress { + internal const int IPv4AddressBytes = 4; + internal const int IPv6AddressBytes = 16; + + internal const int NumberOfLabels = IPv6AddressBytes / 2; + /// /// Provides an IP address that indicates that the server must listen for client activity on all network interfaces. This field is read-only. /// @@ -28,16 +33,25 @@ public class IPAddress internal readonly long Address; + /// + /// The Bind(EndPoint) method uses the IPv6Any field to indicate that a Socket must listen for client activity on all network interfaces. + /// + 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); + + /// + /// Provides the IP loopback address. This property is read-only. + /// + 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); + + [Diagnostics.DebuggerBrowsable(Diagnostics.DebuggerBrowsableState.Never)] private readonly AddressFamily _family = AddressFamily.InterNetwork; [Diagnostics.DebuggerBrowsable(Diagnostics.DebuggerBrowsableState.Never)] private readonly ushort[] _numbers = new ushort[NumberOfLabels]; - internal const int IPv4AddressBytes = 4; - internal const int IPv6AddressBytes = 16; - - internal const int NumberOfLabels = IPv6AddressBytes / 2; + [Diagnostics.DebuggerBrowsable(Diagnostics.DebuggerBrowsableState.Never)] + private long _scopeid = 0; /// /// Gets the address family of the IP address. @@ -81,7 +95,7 @@ public IPAddress(long newAddress) /// /// Initializes a new instance of the class with the address specified as a Byte array. /// - /// + /// The byte array value of the IP address. /// is . /// contains a bad IP address. /// @@ -122,6 +136,40 @@ public IPAddress(byte[] address) } } + /// + /// Initializes a new instance of a IPV6 class with the address specified as a Byte array. + /// + /// The byte array value of the IP address. + /// The long value of the scope identifier. + /// is . + /// contains a bad IP address. + /// + /// The IPAddress is created with the property set to . + /// If the length of is 4, (Byte[]) constructs an IPv4 address; otherwise, an IPv6 address with a scope of 0 is constructed. + /// The array is assumed to be in network byte order with the most significant byte first in index position 0. + /// + public IPAddress(byte[] address, long scopeid) : this(address) + { + if (address.Length == IPv6AddressBytes) + { + _scopeid = scopeid; + } + else + { + // Not IPV6 address +#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one + throw new ArgumentException(); +#pragma warning restore S3928 // OK to throw this here + } + } + + private IPAddress(ushort[] address, uint scopeid) + { + _family = AddressFamily.InterNetworkV6; + _numbers = address; + _scopeid = scopeid; + } + /// /// Compares two IP addresses. /// @@ -133,7 +181,31 @@ public override bool Equals(object obj) if (obj == null) return false; - return this.Address == addr.Address; + // Compare family before address + if (_family != addr.AddressFamily) + { + return false; + } + + if (_family == AddressFamily.InterNetworkV6) + { + // For IPv6 addresses, compare the full 128bit address + for (int i = 0; i < NumberOfLabels; i++) + { + if (addr._numbers[i] != this._numbers[i]) + return false; + } + + // Also scope must match + if (addr._scopeid == this._scopeid) + return true; + + return false; + } + else + { + return this.Address == addr.Address; + } } /// @@ -142,13 +214,30 @@ public override bool Equals(object obj) /// A Byte array. public byte[] GetAddressBytes() { - return new byte[] + byte[] bytes; + + if (_family == AddressFamily.InterNetworkV6) { - (byte)(Address), - (byte)(Address >> 8), - (byte)(Address >> 16), - (byte)(Address >> 24) - }; + bytes = new byte[NumberOfLabels * 2]; + + int j = 0; + for (int i = 0; i < NumberOfLabels; i++) + { + bytes[j++] = (byte)((this._numbers[i] >> 8) & 0xFF); + bytes[j++] = (byte)((this._numbers[i]) & 0xFF); + } + return bytes; + } + else + { + return new byte[] + { + (byte)(Address), + (byte)(Address >> 8), + (byte)(Address >> 16), + (byte)(Address >> 24) + }; + } } /// @@ -159,7 +248,40 @@ public byte[] GetAddressBytes() /// public static IPAddress Parse(string ipString) { - return new IPAddress(NetworkInterface.IPAddressFromString(ipString)); + // Check for IPV6 string and use separate parse method + if (ipString.IndexOf(':') != -1) + { + return new IPAddress(NetworkInterface.IPV6AddressFromString(ipString), 0); + } + + return new IPAddress(NetworkInterface.IPV4AddressFromString(ipString)); + } + + /// + /// Get or Set IPV6 scope identifier. + /// + public long ScopeId + { + get + { + // Not valid for IPv4 addresses + if (_family == AddressFamily.InterNetwork) + { + throw new SocketException(SocketError.OperationNotSupported); + } + + return _scopeid; + } + set + { + // Not valid for IPv4 addresses + if (_family == AddressFamily.InterNetwork) + { + throw new SocketException(SocketError.OperationNotSupported); + } + + _scopeid = value; + } } /// @@ -172,6 +294,11 @@ public static IPAddress Parse(string ipString) /// public override string ToString() { + if (_family == AddressFamily.InterNetworkV6) + { + return IPv6ToString(_numbers); + } + return IPv4ToString((uint)Address); } @@ -229,18 +356,100 @@ internal IPAddress Snapshot() case AddressFamily.InterNetwork: return new IPAddress(Address); - //case AddressFamily.InterNetworkV6: - // return new IPAddress(m_Numbers, (uint)m_ScopeId); + case AddressFamily.InterNetworkV6: + return new IPAddress(_numbers, (uint)_scopeid); } throw new NotSupportedException(); } + /// + /// Gets whether the IP address is an IPv4-mapped IPv6 address. + /// + /// + /// true if the IP address is an IPv4-mapped IPv6 address; otherwise, false. + /// + public bool IsIPv4MappedToIPv6 + { + get + { + if (AddressFamily != AddressFamily.InterNetworkV6) + { + return false; + } + + for (int i = 0; i < 5; i++) + { + if (_numbers[i] != 0) + { + return false; + } + } + + return (_numbers[5] == 0xFFFF); + } + } + + /// + /// Maps the object to an IPv6 address. + /// + /// + /// Dual-stack sockets always require IPv6 addresses. The ability to interact with an IPv4 address + /// requires the use of the IPv4-mapped IPv6 address format. Any IPv4 addresses must be represented + /// in the IPv4-mapped IPv6 address format which enables an IPv6 only application to communicate + /// with an IPv4 node. + /// For example. IPv4 192.168.1.4 maps as IPV6 ::FFFF:192.168.1.4 + /// + /// + /// Returns . An IPV6 address. + /// + public IPAddress MapToIPv6() + { + if (AddressFamily == AddressFamily.InterNetworkV6) + { + return this; + } + + ushort[] labels = new ushort[IPAddress.NumberOfLabels]; + labels[5] = 0xFFFF; + labels[6] = (ushort)(((Address & 0x0000FF00) >> 8) | ((Address & 0x000000FF) << 8)); + labels[7] = (ushort)(((Address & 0xFF000000) >> 24) | ((Address & 0x00FF0000) >> 8)); + + return new IPAddress(labels, 0); + } + + /// + /// Maps the object to an IPv4 address. + /// + /// + /// Dual-stack sockets always require IPv6 addresses. The ability to interact with an IPv4 + /// address requires the use of the IPv4-mapped IPv6 address format. + /// + /// + /// Returns . An IPV4 address. + /// + public IPAddress MapToIPv4() + { + if (AddressFamily == AddressFamily.InterNetwork) + { + return this; + } + + long address = ((((uint)_numbers[6] & 0x0000FF00u) >> 8) | + (((uint)_numbers[6] & 0x000000FFu) << 8)) | + (((((uint)_numbers[7] & 0x0000FF00u) >> 8) | + (((uint)_numbers[7] & 0x000000FFu) << 8)) << 16); + + return new IPAddress(address); + } + #region native methods [MethodImpl(MethodImplOptions.InternalCall)] internal static extern string IPv4ToString(uint ipv4Address); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern string IPv6ToString(ushort[] ipv6Address); #endregion } } diff --git a/nanoFramework.System.Net/NetworkInformation/NetworkInterface.cs b/nanoFramework.System.Net/NetworkInformation/NetworkInterface.cs index 04e51ee..3c5e72f 100644 --- a/nanoFramework.System.Net/NetworkInformation/NetworkInterface.cs +++ b/nanoFramework.System.Net/NetworkInformation/NetworkInterface.cs @@ -126,9 +126,9 @@ public void EnableStaticIPv4(string ipv4Address, string ipv4SubnetMask, string i { try { - _ipv4Address = (uint)IPAddressFromString(ipv4Address); - _ipv4NetMask = (uint)IPAddressFromString(ipv4SubnetMask); - _ipv4GatewayAddress = (uint)IPAddressFromString(ipv4GatewayAddress); + _ipv4Address = (uint)IPV4AddressFromString(ipv4Address); + _ipv4NetMask = (uint)IPV4AddressFromString(ipv4SubnetMask); + _ipv4GatewayAddress = (uint)IPV4AddressFromString(ipv4GatewayAddress); _startupAddressMode = AddressMode.Static; UpdateConfiguration((int)UpdateOperation.Dhcp); @@ -153,9 +153,9 @@ public void EnableStaticIPv6(string ipv6Address, string ipv6SubnetMask, string i // FIXME // need to test this - //_ipv6Address = IPAddressFromString(ipv6Address); - //_ipv6NetMask = IPAddressFromString(ipv6subnetMask); - //_ipv6GatewayAddress = IPAddressFromString(ipv6gatewayAddress); + //_ipv6Address = IPV4AddressFromString(ipv6Address); + //_ipv6NetMask = IPV4AddressFromString(ipv6subnetMask); + //_ipv6GatewayAddress = IPV4AddressFromString(ipv6gatewayAddress); //_startupAddressMode = AddressMode.Static; @@ -182,15 +182,15 @@ public void EnableStaticIP(string ipv4Address, string ipv4subnetMask, string ipv { throw new NotImplementedException(); - _ipv4Address = (uint)IPAddressFromString(ipv4Address); - _ipv4NetMask = (uint)IPAddressFromString(ipv4subnetMask); - _ipv4GatewayAddress = (uint)IPAddressFromString(ipv4gatewayAddress); + _ipv4Address = (uint)IPV4AddressFromString(ipv4Address); + _ipv4NetMask = (uint)IPV4AddressFromString(ipv4subnetMask); + _ipv4GatewayAddress = (uint)IPV4AddressFromString(ipv4gatewayAddress); // FIXME // need to test this - //_ipv6Address = IPAddressFromString(ipv6Address); - //_ipv6NetMask = IPAddressFromString(ipv6subnetMask); - //_ipv6GatewayAddress = IPAddressFromString(ipv6gatewayAddress); + //_ipv6Address = IPV4AddressFromString(ipv6Address); + //_ipv6NetMask = IPV4AddressFromString(ipv6subnetMask); + //_ipv6GatewayAddress = IPV4AddressFromString(ipv6gatewayAddress); _startupAddressMode = AddressMode.Static; @@ -234,7 +234,7 @@ public void EnableStaticIPv4Dns(string[] dnsAddresses) int iAddress = 0; for (int i = 0; i < dnsAddresses.Length; i++) { - uint address = (uint)IPAddressFromString(dnsAddresses[i]); + uint address = (uint)IPV4AddressFromString(dnsAddresses[i]); addresses[iAddress] = address; @@ -281,7 +281,7 @@ public void EnableStaticIPv6Dns(string[] dnsAddresses) //int iAddress = 0; //for (int i = 0; i < dnsAddresses.Length; i++) //{ - // uint address = IPAddressFromString(dnsAddresses[i]); + // uint address = IPV4AddressFromString(dnsAddresses[i]); // addresses[iAddress] = address; @@ -482,8 +482,10 @@ public NetworkInterfaceType NetworkInterfaceType private extern void UpdateConfiguration(int updateType); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern long IPAddressFromString(string ipAddress); + internal static extern long IPV4AddressFromString(string ipAddress); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern ushort[] IPV6AddressFromString(string ipAddress); #endregion } } diff --git a/nanoFramework.System.Net/NetworkInformation/WirelessAPConfiguration.cs b/nanoFramework.System.Net/NetworkInformation/WirelessAPConfiguration.cs index 2c04d6a..bd6973b 100644 --- a/nanoFramework.System.Net/NetworkInformation/WirelessAPConfiguration.cs +++ b/nanoFramework.System.Net/NetworkInformation/WirelessAPConfiguration.cs @@ -125,7 +125,7 @@ public WirelessAPConfiguration(uint id) /// /// If is less than one. /// If is less than 8 character or more than 64 characters and not open . - /// If is longer than <./exception> + /// If is longer than /// If is . public void SaveConfiguration() { @@ -140,7 +140,7 @@ public void SaveConfiguration() /// /// If is less than one. /// If is less than 8 character or more than 64 characters and not open . - /// If is longer than <./exception> + /// If is longer than /// If is . private void ValidateConfiguration() { diff --git a/nanoFramework.System.Net/Properties/AssemblyInfo.cs b/nanoFramework.System.Net/Properties/AssemblyInfo.cs index 3642cc8..6f37577 100644 --- a/nanoFramework.System.Net/Properties/AssemblyInfo.cs +++ b/nanoFramework.System.Net/Properties/AssemblyInfo.cs @@ -11,7 +11,7 @@ //////////////////////////////////////////////////////////////// // update this whenever the native assembly signature changes // -[assembly: AssemblyNativeVersion("100.1.5.0")] +[assembly: AssemblyNativeVersion("100.2.0.0")] //////////////////////////////////////////////////////////////// // Setting ComVisible to false makes the types in this assembly not visible diff --git a/nanoFramework.System.Net/SocketAddress.cs b/nanoFramework.System.Net/SocketAddress.cs index aceb6a3..557eb1c 100644 --- a/nanoFramework.System.Net/SocketAddress.cs +++ b/nanoFramework.System.Net/SocketAddress.cs @@ -48,29 +48,29 @@ internal SocketAddress(IPAddress ipAddress) m_Buffer[2] = 0; m_Buffer[3] = 0; - //if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) - //{ - // // No handling for Flow Information - // m_Buffer[4] = 0; - // m_Buffer[5] = 0; - // m_Buffer[6] = 0; - // m_Buffer[7] = 0; - - // // Scope serialization - // long scope = ipAddress.ScopeId; - // m_Buffer[24] = (byte)scope; - // m_Buffer[25] = (byte)(scope >> 8); - // m_Buffer[26] = (byte)(scope >> 16); - // m_Buffer[27] = (byte)(scope >> 24); - - // // Address serialization - // byte[] addressBytes = ipAddress.GetAddressBytes(); - // for (int i = 0; i < addressBytes.Length; i++) - // { - // m_Buffer[8 + i] = addressBytes[i]; - // } - //} - //else + if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) + { + // No handling for Flow Information + m_Buffer[4] = 0; + m_Buffer[5] = 0; + m_Buffer[6] = 0; + m_Buffer[7] = 0; + + // Scope serialization + long scope = ipAddress.ScopeId; + m_Buffer[24] = (byte)scope; + m_Buffer[25] = (byte)(scope >> 8); + m_Buffer[26] = (byte)(scope >> 16); + m_Buffer[27] = (byte)(scope >> 24); + + // Address serialization + byte[] addressBytes = ipAddress.GetAddressBytes(); + for (int i = 0; i < addressBytes.Length; i++) + { + m_Buffer[8 + i] = addressBytes[i]; + } + } + else { // IPv4 Address serialization m_Buffer[4] = unchecked((byte)(ipAddress.Address)); @@ -187,33 +187,30 @@ public override int GetHashCode() internal IPAddress GetIPAddress() { - //if (Family == AddressFamily.InterNetworkV6) - //{ - // byte[] address = new byte[IPAddress.IPv6AddressBytes]; - // for (int i = 0; i < address.Length; i++) - // { - // address[i] = m_Buffer[i + 8]; - // } - - // long scope = (long)((m_Buffer[27] << 24) + - // (m_Buffer[26] << 16) + - // (m_Buffer[25] << 8) + - // (m_Buffer[24])); - - // return new IPAddress(address, scope); - - //} - //else if (Family == AddressFamily.InterNetwork) + if (Family == AddressFamily.InterNetworkV6) { - long address = ( - (m_Buffer[4] & 0x000000FF) | - (m_Buffer[5] << 8 & 0x0000FF00) | - (m_Buffer[6] << 16 & 0x00FF0000) | - (m_Buffer[7] << 24) - ) & 0x00000000FFFFFFFF; - - return new IPAddress(address); + byte[] addr = new byte[IPAddress.IPv6AddressBytes]; + for (int i = 0; i < addr.Length; i++) + { + addr[i] = m_Buffer[i + 8]; + } + + long scope = (long)((m_Buffer[27] << 24) + + (m_Buffer[26] << 16) + + (m_Buffer[25] << 8) + + (m_Buffer[24])); + + return new IPAddress(addr, scope); } + + long address = ( + (m_Buffer[4] & 0x000000FF) | + (m_Buffer[5] << 8 & 0x0000FF00) | + (m_Buffer[6] << 16 & 0x00FF0000) | + (m_Buffer[7] << 24) + ) & 0x00000000FFFFFFFF; + + return new IPAddress(address); } } } diff --git a/nanoFramework.System.Net/Sockets/NetworkStream.cs b/nanoFramework.System.Net/Sockets/NetworkStream.cs index 46d11ba..0eb6755 100644 --- a/nanoFramework.System.Net/Sockets/NetworkStream.cs +++ b/nanoFramework.System.Net/Sockets/NetworkStream.cs @@ -124,7 +124,7 @@ public NetworkStream(Socket socket, bool ownsSocket) /// true if data can be read from the stream; otherwise, false. The default value is true. /// /// If CanRead is true, allows calls to the method. Provide the appropriate FileAccess enumerated value in the constructor to set - /// the readability and writability of the . The CanRead property is set when the is initialized. + /// the readability and write-ability of the . The CanRead property is set when the is initialized. /// public override bool CanRead { get { return true; } } diff --git a/nanoFramework.System.Net/Sockets/Socket.cs b/nanoFramework.System.Net/Sockets/Socket.cs index 7f478a8..3dd7969 100644 --- a/nanoFramework.System.Net/Sockets/Socket.cs +++ b/nanoFramework.System.Net/Sockets/Socket.cs @@ -61,7 +61,7 @@ public class Socket : IDisposable /// The addressFamily parameter specifies the addressing scheme that the class uses, the socketType parameter specifies the type of the class, /// and the protocolType parameter specifies the protocol used by . The three parameters are not independent. Some address families restrict which /// protocols can be used with them, and often the type is implicit in the protocol. If the combination of address family, type, and protocol type - /// esults in an invalid Socket, this constructor throws a SocketException. + /// results in an invalid Socket, this constructor throws a SocketException. /// public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) { @@ -1100,9 +1100,6 @@ void IDisposable.Dispose() GC.SuppressFinalize(this); } - /// - /// Deconstructor - /// ~Socket() { Dispose(false);