// SNMP IP address type. // Copyright (C) 2008-2010 Malcolm Crowe, Lex Li, and other contributors. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Globalization; using System.IO; using System.Linq; namespace GSF.Net.Snmp { /// /// IPAddress type. /// public sealed class IP : ISnmpData, IEquatable { private readonly byte[] _ip; private readonly byte[] _length; private const int IPv4Length = 4; /// /// Initializes a new instance of the class. /// /// The IP bytes. /// is null. public IP(byte[] ip) { if (ip == null) { throw new ArgumentNullException(nameof(ip)); } _ip = ip; } /// /// Creates a new instance of the class from a specific . /// /// IP string public IP(string ip) { // IMPORTANT: copied from Mono's IPAddress.cs int pos = ip.IndexOf(' '); if (pos != -1) { string[] nets = ip.Substring(pos + 1).Split(new char[] { '.' }); if (nets.Length > 0) { string lastNet = nets[nets.Length - 1]; if (lastNet.Length == 0) throw new FormatException("An invalid IP address was specified."); #if NET_2_1 //workaround for smcs, as it generate code that can't access string.GetEnumerator () foreach (char c in lastNet.ToCharArray ()) #else foreach (char c in lastNet) #endif if (!IsHexDigit(c)) throw new FormatException("An invalid IP address was specified."); } ip = ip.Substring(0, pos); } if (ip.Length == 0 || ip[ip.Length - 1] == '.') throw new FormatException("An invalid IP address was specified."); string[] ips = ip.Split(new char[] { '.' }); if (ips.Length > IPv4Length) throw new FormatException("An invalid IP address was specified."); // Make the number in network order try { _ip = new byte[IPv4Length]; long val = 0; for (int i = 0; i < ips.Length; i++) { string subnet = ips[i]; if ((3 <= subnet.Length && subnet.Length <= 4) && (subnet[0] == '0') && (subnet[1] == 'x' || subnet[1] == 'X')) { if (subnet.Length == 3) val = (byte)FromHex(subnet[2]); else val = (byte)((FromHex(subnet[2]) << 4) | FromHex(subnet[3])); } else if (subnet.Length == 0) throw new FormatException("An invalid IP address was specified."); else if (subnet[0] == '0') { // octal val = 0; for (int j = 1; j < subnet.Length; j++) { if ('0' <= subnet[j] && subnet[j] <= '7') val = (val << 3) + subnet[j] - '0'; else throw new FormatException("An invalid IP address was specified."); } } else { if (!long.TryParse(subnet, NumberStyles.None, null, out val)) throw new FormatException("An invalid IP address was specified."); } if (i == (ips.Length - 1)) { if (i != 0 && val >= (256 << ((3 - i) * 8))) throw new FormatException("An invalid IP address was specified."); else if (val > 0x3fffffffe) // this is the last number that parses correctly with MS throw new FormatException("An invalid IP address was specified."); i = 3; } else if (val >= 0x100) throw new FormatException("An invalid IP address was specified."); _ip[i] = (byte)val; } } catch (Exception) { throw new FormatException("An invalid IP address was specified."); } } private static int FromHex(char digit) { if ('0' <= digit && digit <= '9') { return (int)(digit - '0'); } if ('a' <= digit && digit <= 'f') return (int)(digit - 'a' + 10); if ('A' <= digit && digit <= 'F') return (int)(digit - 'A' + 10); throw new ArgumentException("Invalid digit.", nameof(digit)); } private static bool IsHexDigit(char character) { return (('0' <= character && character <= '9') || ('a' <= character && character <= 'f') || ('A' <= character && character <= 'F')); } /// /// Initializes a new instance of the class. /// /// The length. /// The stream. public IP(Tuple length, Stream stream) { if (length == null) { throw new ArgumentNullException(nameof(length)); } if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (length.Item1 != IPv4Length) { throw new ArgumentException("Bytes must contain 4 or 16 elements.", nameof(length)); } byte[] raw = new byte[length.Item1]; stream.Read(raw, 0, length.Item1); _ip = raw; _length = length.Item2; } /// /// Gets the raw bytes. /// /// System.Byte[]. public byte[] GetRaw() { return _ip; } /// /// Returns a that represents this . /// /// public override string ToString() { return string.Format("{0}.{1}.{2}.{3}", _ip[0], _ip[1], _ip[2], _ip[3]); } /// /// Type code. /// public SnmpType TypeCode { get { return SnmpType.IPAddress; } } /// /// Appends the bytes to . /// /// The stream. public void AppendBytesTo(Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } stream.AppendBytes(TypeCode, _length, _ip); } /// /// Determines whether the specified is equal to the current . /// /// The to compare with the current . /// true if the specified is equal to the current ; otherwise, false. /// public override bool Equals(object obj) { return Equals(this, obj as IP); } /// /// Indicates whether the current object is equal to another object of the same type. /// /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. /// public bool Equals(IP other) { return Equals(this, other); } /// /// Serves as a hash function for a particular type. /// /// A hash code for the current . public override int GetHashCode() { return _ip.GetHashCode(); } /// /// The equality operator. /// /// Left object /// Right object /// /// Returns true if the values of its operands are equal, false otherwise. public static bool operator ==(IP left, IP right) { return Equals(left, right); } /// /// The inequality operator. /// /// Left object /// Right object /// /// Returns true if the values of its operands are not equal, false otherwise. public static bool operator !=(IP left, IP right) { return !(left == right); } /// /// The comparison. /// /// Left object /// Right object /// /// Returns true if the values of its operands are not equal, false otherwise. private static bool Equals(IP left, IP right) { object lo = left; object ro = right; if (lo == ro) { return true; } if (lo == null || ro == null) { return false; } return left._ip.SequenceEqual(right._ip); } } }