< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 150
Coverable lines: 150
Total lines: 378
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 74
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Primitives/src/System/Net/IPNetwork.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers.Binary;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Globalization;
 8using System.Net.Sockets;
 9using System.Runtime.InteropServices;
 10using System.Text.Unicode;
 11
 12namespace System.Net
 13{
 14    /// <summary>
 15    /// Represents an IP network with an <see cref="IPAddress"/> containing the network prefix and an <see cref="int"/> 
 16    /// </summary>
 17    /// <remarks>
 18    /// This type disallows arbitrary IP-address/prefix-length CIDR pairs. <see cref="BaseAddress"/> must be defined so 
 19    /// In other words, <see cref="BaseAddress"/> is always the first usable address of the network.
 20    /// The constructor and the parsing methods will throw in case there are non-zero bits after the prefix.
 21    /// </remarks>
 22    public readonly struct IPNetwork : IEquatable<IPNetwork>, ISpanFormattable, ISpanParsable<IPNetwork>, IUtf8SpanForma
 23    {
 24        private readonly IPAddress? _baseAddress;
 25
 26        /// <summary>
 27        /// Gets the <see cref="IPAddress"/> that represents the prefix of the network.
 28        /// </summary>
 029        public IPAddress BaseAddress => _baseAddress ?? IPAddress.Any;
 30
 31        /// <summary>
 32        /// Gets the length of the network prefix in bits.
 33        /// </summary>
 034        public int PrefixLength { get; }
 35
 36        /// <summary>
 37        /// Initializes a new instance of the <see cref="IPNetwork"/> class with the specified <see cref="IPAddress"/> a
 38        /// </summary>
 39        /// <param name="baseAddress">The <see cref="IPAddress"/> that represents the prefix of the network.</param>
 40        /// <param name="prefixLength">The length of the prefix in bits.</param>
 41        /// <exception cref="ArgumentNullException">The specified <paramref name="baseAddress"/> is <see langword="null"
 42        /// <exception cref="ArgumentOutOfRangeException">The specified <paramref name="prefixLength"/> is smaller than 
 43        /// <exception cref="ArgumentException">The specified <paramref name="baseAddress"/> has non-zero bits after the
 44        public IPNetwork(IPAddress baseAddress, int prefixLength)
 045        {
 046            ArgumentNullException.ThrowIfNull(baseAddress);
 47
 048            if (prefixLength < 0 || prefixLength > GetMaxPrefixLength(baseAddress))
 049            {
 050                ThrowArgumentOutOfRangeException();
 51            }
 52
 053            _baseAddress = ClearNonZeroBitsAfterNetworkPrefix(baseAddress, prefixLength);
 054            PrefixLength = prefixLength;
 55
 56            [DoesNotReturn]
 057            static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(prefixLength)
 058        }
 59
 60        /// <summary>
 61        /// Determines whether a given <see cref="IPAddress"/> is part of the network.
 62        /// </summary>
 63        /// <param name="address">The <see cref="IPAddress"/> to check.</param>
 64        /// <returns><see langword="true"/> if the <see cref="IPAddress"/> is part of the network; otherwise, <see langw
 65        /// <exception cref="ArgumentNullException">The specified <paramref name="address"/> is <see langword="null"/>.<
 66        public bool Contains(IPAddress address)
 067        {
 068            ArgumentNullException.ThrowIfNull(address);
 69
 070            if (address.AddressFamily != BaseAddress.AddressFamily && (BaseAddress.AddressFamily != AddressFamily.InterN
 071            {
 072                return false;
 73            }
 74
 75            // This prevents the 'uint.MaxValue << 32' and the 'UInt128.MaxValue << 128' special cases in the code below
 076            if (PrefixLength == 0)
 077            {
 078                return true;
 79            }
 80
 081            if (BaseAddress.AddressFamily == AddressFamily.InterNetwork)
 082            {
 083                uint mask = uint.MaxValue << (32 - PrefixLength);
 084                if (BitConverter.IsLittleEndian)
 085                {
 086                    mask = BinaryPrimitives.ReverseEndianness(mask);
 087                }
 88
 089                return BaseAddress.PrivateIPv4Address == (address.PrivateIPv4Address & mask);
 90            }
 91            else
 092            {
 093                UInt128 baseAddressValue = default;
 094                UInt128 otherAddressValue = default;
 95
 096                BaseAddress.TryWriteBytes(MemoryMarshal.AsBytes(new Span<UInt128>(ref baseAddressValue)), out int bytesW
 097                Debug.Assert(bytesWritten == IPAddressParserStatics.IPv6AddressBytes);
 098                address.TryWriteBytes(MemoryMarshal.AsBytes(new Span<UInt128>(ref otherAddressValue)), out bytesWritten)
 099                Debug.Assert(bytesWritten == IPAddressParserStatics.IPv6AddressBytes);
 100
 0101                UInt128 mask = UInt128.MaxValue << (128 - PrefixLength);
 0102                if (BitConverter.IsLittleEndian)
 0103                {
 0104                    mask = BinaryPrimitives.ReverseEndianness(mask);
 0105                }
 106
 0107                return baseAddressValue == (otherAddressValue & mask);
 108            }
 0109        }
 110
 111        /// <summary>
 112        /// Converts a CIDR <see cref="string"/> to an <see cref="IPNetwork"/> instance.
 113        /// </summary>
 114        /// <param name="s">A <see cref="string"/> that defines an IP network in CIDR notation.</param>
 115        /// <returns>An <see cref="IPNetwork"/> instance.</returns>
 116        /// <exception cref="ArgumentNullException">The specified string is <see langword="null"/>.</exception>
 117        /// <exception cref="FormatException"><paramref name="s"/> is not a valid CIDR network string, or the address co
 118        public static IPNetwork Parse(string s)
 0119        {
 0120            ArgumentNullException.ThrowIfNull(s);
 0121            return Parse(s.AsSpan());
 0122        }
 123
 124        /// <summary>
 125        /// Converts a CIDR character span to an <see cref="IPNetwork"/> instance.
 126        /// </summary>
 127        /// <param name="s">A character span that defines an IP network in CIDR notation.</param>
 128        /// <returns>An <see cref="IPNetwork"/> instance.</returns>
 129        /// <exception cref="FormatException"><paramref name="s"/> is not a valid CIDR network string, or the address co
 130        public static IPNetwork Parse(ReadOnlySpan<char> s)
 0131        {
 0132            if (!TryParse(s, out IPNetwork result))
 0133            {
 0134                throw new FormatException(SR.net_bad_ip_network);
 135            }
 136
 0137            return result;
 0138        }
 139
 140        /// <summary>
 141        /// Converts a UTF-8 CIDR character span to an <see cref="IPNetwork"/> instance.
 142        /// </summary>
 143        /// <param name="utf8Text">A UTF-8 character span that defines an IP network in CIDR notation.</param>
 144        /// <returns>An <see cref="IPNetwork"/> instance.</returns>
 145        /// <exception cref="FormatException"><paramref name="utf8Text"/> is not a valid UTF-8 CIDR network string, or t
 146        public static IPNetwork Parse(ReadOnlySpan<byte> utf8Text)
 0147        {
 0148            if (!TryParse(utf8Text, out IPNetwork result))
 0149            {
 0150                throw new FormatException(SR.net_bad_ip_network);
 151            }
 152
 0153            return result;
 0154        }
 155
 156        /// <summary>
 157        /// Converts the specified CIDR string to an <see cref="IPNetwork"/> instance and returns a value indicating whe
 158        /// </summary>
 159        /// <param name="s">A <see cref="string"/> that defines an IP network in CIDR notation.</param>
 160        /// <param name="result">When the method returns, contains an <see cref="IPNetwork"/> instance if the conversion
 161        /// <returns><see langword="true"/> if the conversion was succesful; otherwise, <see langword="false"/>.</return
 162        public static bool TryParse(string? s, out IPNetwork result)
 0163        {
 0164            if (s == null)
 0165            {
 0166                result = default;
 0167                return false;
 168            }
 169
 0170            return TryParse(s.AsSpan(), out result);
 0171        }
 172
 173        /// <summary>
 174        /// Converts the specified CIDR character span to an <see cref="IPNetwork"/> instance and returns a value indica
 175        /// </summary>
 176        /// <param name="s">A <see cref="string"/> that defines an IP network in CIDR notation.</param>
 177        /// <param name="result">When the method returns, contains an <see cref="IPNetwork"/> instance if the conversion
 178        /// <returns><see langword="true"/> if the conversion was successful; otherwise, <see langword="false"/>.</retur
 179        public static bool TryParse(ReadOnlySpan<char> s, out IPNetwork result)
 0180        {
 0181            int separatorIndex = s.LastIndexOf('/');
 0182            if (separatorIndex >= 0)
 0183            {
 0184                ReadOnlySpan<char> ipAddressSpan = s.Slice(0, separatorIndex);
 0185                ReadOnlySpan<char> prefixLengthSpan = s.Slice(separatorIndex + 1);
 186
 0187                if (IPAddress.TryParse(ipAddressSpan, out IPAddress? address) &&
 0188                    int.TryParse(prefixLengthSpan, NumberStyles.None, CultureInfo.InvariantCulture, out int prefixLength
 0189                    prefixLength <= GetMaxPrefixLength(address))
 0190                {
 0191                    Debug.Assert(prefixLength >= 0); // Parsing with NumberStyles.None should ensure that prefixLength i
 0192                    result = new IPNetwork(address, prefixLength);
 0193                    return true;
 194                }
 0195            }
 196
 0197            result = default;
 0198            return false;
 0199        }
 200
 201        /// <summary>
 202        /// Converts the specified UTF-8 CIDR character span to an <see cref="IPNetwork"/> instance and returns a value 
 203        /// </summary>
 204        /// <param name="utf8Text">A UTF-8 character span that defines an IP network in CIDR notation.</param>
 205        /// <param name="result">When the method returns, contains an <see cref="IPNetwork"/> instance if the conversion
 206        /// <returns><see langword="true"/> if the conversion was successful; otherwise, <see langword="false"/>.</retur
 207        public static bool TryParse(ReadOnlySpan<byte> utf8Text, out IPNetwork result)
 0208        {
 0209            int separatorIndex = utf8Text.LastIndexOf((byte)'/');
 0210            if (separatorIndex >= 0)
 0211            {
 0212                ReadOnlySpan<byte> ipAddressSpan = utf8Text.Slice(0, separatorIndex);
 0213                ReadOnlySpan<byte> prefixLengthSpan = utf8Text.Slice(separatorIndex + 1);
 214
 0215                if (IPAddress.TryParse(ipAddressSpan, out IPAddress? address) &&
 0216                    int.TryParse(prefixLengthSpan, NumberStyles.None, CultureInfo.InvariantCulture, out int prefixLength
 0217                    prefixLength <= GetMaxPrefixLength(address))
 0218                {
 0219                    Debug.Assert(prefixLength >= 0); // Parsing with NumberStyles.None should ensure that prefixLength i
 0220                    result = new IPNetwork(address, prefixLength);
 0221                    return true;
 222                }
 0223            }
 224
 0225            result = default;
 0226            return false;
 0227        }
 228
 0229        private static int GetMaxPrefixLength(IPAddress baseAddress) => baseAddress.AddressFamily == AddressFamily.Inter
 230
 231        private static IPAddress ClearNonZeroBitsAfterNetworkPrefix(IPAddress baseAddress, int prefixLength)
 0232        {
 0233            if (baseAddress.AddressFamily == AddressFamily.InterNetwork)
 0234            {
 235                // Bitwise shift works only for lower 5-bits count operands.
 0236                if (prefixLength == 0)
 0237                {
 238                    // Corresponds to 0.0.0.0
 0239                    return IPAddress.Any;
 240                }
 241
 0242                uint mask = uint.MaxValue << (32 - prefixLength);
 0243                if (BitConverter.IsLittleEndian)
 0244                {
 0245                    mask = BinaryPrimitives.ReverseEndianness(mask);
 0246                }
 247
 0248                uint newAddress = baseAddress.PrivateAddress & mask;
 0249                return newAddress == baseAddress.PrivateAddress
 0250                    ? baseAddress
 0251                    : new IPAddress(newAddress);
 252            }
 253            else
 0254            {
 255                // Bitwise shift works only for lower 7-bits count operands.
 0256                if (prefixLength == 0)
 0257                {
 258                    // Corresponds to [::]
 0259                    return IPAddress.IPv6Any;
 260                }
 261
 0262                UInt128 value = default;
 0263                baseAddress.TryWriteBytes(MemoryMarshal.AsBytes(new Span<UInt128>(ref value)), out int bytesWritten);
 0264                Debug.Assert(bytesWritten == IPAddressParserStatics.IPv6AddressBytes);
 265
 0266                UInt128 mask = UInt128.MaxValue << (128 - prefixLength);
 0267                if (BitConverter.IsLittleEndian)
 0268                {
 0269                    mask = BinaryPrimitives.ReverseEndianness(mask);
 0270                }
 271
 0272                UInt128 newAddress = value & mask;
 0273                return newAddress == value
 0274                    ? baseAddress
 0275                    : new IPAddress(MemoryMarshal.AsBytes(new Span<UInt128>(ref newAddress)));
 276            }
 0277        }
 278
 279        /// <summary>
 280        /// Converts the instance to a string containing the <see cref="IPNetwork"/>'s CIDR notation.
 281        /// </summary>
 282        /// <returns>The <see cref="string"/> containing the <see cref="IPNetwork"/>'s CIDR notation.</returns>
 283        public override string ToString() =>
 0284            string.Create(CultureInfo.InvariantCulture, stackalloc char[128], $"{BaseAddress}/{(uint)PrefixLength}");
 285
 286        /// <summary>
 287        /// Attempts to write the <see cref="IPNetwork"/>'s CIDR notation to the given <paramref name="destination"/> sp
 288        /// </summary>
 289        /// <param name="destination">The destination span of characters.</param>
 290        /// <param name="charsWritten">When this method returns, contains the number of characters that were written to 
 291        /// <returns><see langword="true"/> if the formatting was succesful; otherwise <see langword="false"/>.</returns
 292        public bool TryFormat(Span<char> destination, out int charsWritten) =>
 0293            destination.TryWrite(CultureInfo.InvariantCulture, $"{BaseAddress}/{(uint)PrefixLength}", out charsWritten);
 294
 295        /// <summary>
 296        /// Attempts to write the <see cref="IPNetwork"/>'s CIDR notation to the given <paramref name="utf8Destination"/
 297        /// </summary>
 298        /// <param name="utf8Destination">The destination span of UTF-8 bytes.</param>
 299        /// <param name="bytesWritten">When this method returns, contains the number of bytes that were written to <para
 300        /// <returns><see langword="true"/> if the formatting was succesful; otherwise <see langword="false"/>.</returns
 301        public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten) =>
 0302            Utf8.TryWrite(utf8Destination, CultureInfo.InvariantCulture, $"{BaseAddress}/{(uint)PrefixLength}", out byte
 303
 304        /// <summary>
 305        /// Determines whether two <see cref="IPNetwork"/> instances are equal.
 306        /// </summary>
 307        /// <param name="other">The <see cref="IPNetwork"/> instance to compare to this instance.</param>
 308        /// <returns><see langword="true"/> if the networks are equal; otherwise <see langword="false"/>.</returns>
 309        /// <exception cref="InvalidOperationException">Uninitialized <see cref="IPNetwork"/> instance.</exception>
 310        public bool Equals(IPNetwork other) =>
 0311            PrefixLength == other.PrefixLength &&
 0312            BaseAddress.Equals(other.BaseAddress);
 313
 314        /// <summary>
 315        /// Determines whether two <see cref="IPNetwork"/> instances are equal.
 316        /// </summary>
 317        /// <param name="obj">The <see cref="IPNetwork"/> instance to compare to this instance.</param>
 318        /// <returns><see langword="true"/> if <paramref name="obj"/> is an <see cref="IPNetwork"/> instance and the net
 319        /// <exception cref="InvalidOperationException">Uninitialized <see cref="IPNetwork"/> instance.</exception>
 320        public override bool Equals([NotNullWhen(true)] object? obj) =>
 0321            obj is IPNetwork other &&
 0322            Equals(other);
 323
 324        /// <summary>
 325        /// Determines whether the specified instances of <see cref="IPNetwork"/> are equal.
 326        /// </summary>
 327        /// <param name="left"></param>
 328        /// <param name="right"></param>
 329        /// <returns><see langword="true"/> if the networks are equal; otherwise <see langword="false"/>.</returns>
 0330        public static bool operator ==(IPNetwork left, IPNetwork right) => left.Equals(right);
 331
 332        /// <summary>
 333        /// Determines whether the specified instances of <see cref="IPNetwork"/> are not equal.
 334        /// </summary>
 335        /// <param name="left"></param>
 336        /// <param name="right"></param>
 337        /// <returns><see langword="true"/> if the networks are not equal; otherwise <see langword="false"/>.</returns>
 0338        public static bool operator !=(IPNetwork left, IPNetwork right) => !(left == right);
 339
 340        /// <summary>
 341        /// Returns the hash code for this instance.
 342        /// </summary>
 343        /// <returns>An integer hash value.</returns>
 0344        public override int GetHashCode() => HashCode.Combine(BaseAddress, PrefixLength);
 345
 346        /// <inheritdoc />
 0347        string IFormattable.ToString(string? format, IFormatProvider? provider) => ToString();
 348
 349        /// <inheritdoc />
 350        bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormat
 351            // format and provider are ignored
 0352            TryFormat(destination, out charsWritten);
 353
 354        /// <inheritdoc />
 355        bool IUtf8SpanFormattable.TryFormat(Span<byte> utf8Destination, out int bytesWritten, ReadOnlySpan<char> format,
 356            // format and provider are ignored
 0357            TryFormat(utf8Destination, out bytesWritten);
 358
 359        /// <inheritdoc />
 0360        static IPNetwork IParsable<IPNetwork>.Parse([NotNull] string s, IFormatProvider? provider) => Parse(s);
 361
 362        /// <inheritdoc />
 0363        static bool IParsable<IPNetwork>.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out IPNetwor
 364
 365        /// <inheritdoc />
 0366        static IPNetwork ISpanParsable<IPNetwork>.Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s);
 367
 368        /// <inheritdoc />
 0369        static IPNetwork IUtf8SpanParsable<IPNetwork>.Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) => P
 370
 371        /// <inheritdoc />
 0372        static bool ISpanParsable<IPNetwork>.TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out IPNetwork res
 373
 374        /// <inheritdoc />
 0375        static bool IUtf8SpanParsable<IPNetwork>.TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, out IP
 376    }
 377}
 378

Methods/Properties

BaseAddress()
PrefixLength()
.ctor(System.Net.IPAddress,System.Int32)
ThrowArgumentOutOfRangeException()
Contains(System.Net.IPAddress)
Parse(System.String)
Parse(System.ReadOnlySpan`1<System.Char>)
Parse(System.ReadOnlySpan`1<System.Byte>)
TryParse(System.String,System.Net.IPNetwork&)
TryParse(System.ReadOnlySpan`1<System.Char>,System.Net.IPNetwork&)
TryParse(System.ReadOnlySpan`1<System.Byte>,System.Net.IPNetwork&)
GetMaxPrefixLength(System.Net.IPAddress)
ClearNonZeroBitsAfterNetworkPrefix(System.Net.IPAddress,System.Int32)
ToString()
TryFormat(System.Span`1<System.Char>,System.Int32&)
TryFormat(System.Span`1<System.Byte>,System.Int32&)
Equals(System.Net.IPNetwork)
Equals(System.Object)
op_Equality(System.Net.IPNetwork,System.Net.IPNetwork)
op_Inequality(System.Net.IPNetwork,System.Net.IPNetwork)
GetHashCode()
System.IFormattable.ToString(System.String,System.IFormatProvider)
System.ISpanFormattable.TryFormat(System.Span`1<System.Char>,System.Int32&,System.ReadOnlySpan`1<System.Char>,System.IFormatProvider)
System.IUtf8SpanFormattable.TryFormat(System.Span`1<System.Byte>,System.Int32&,System.ReadOnlySpan`1<System.Char>,System.IFormatProvider)
System.IParsable<System.Net.IPNetwork>.Parse(System.String,System.IFormatProvider)
System.IParsable<System.Net.IPNetwork>.TryParse(System.String,System.IFormatProvider,System.Net.IPNetwork&)
System.ISpanParsable<System.Net.IPNetwork>.Parse(System.ReadOnlySpan`1<System.Char>,System.IFormatProvider)
System.IUtf8SpanParsable<System.Net.IPNetwork>.Parse(System.ReadOnlySpan`1<System.Byte>,System.IFormatProvider)
System.ISpanParsable<System.Net.IPNetwork>.TryParse(System.ReadOnlySpan`1<System.Char>,System.IFormatProvider,System.Net.IPNetwork&)
System.IUtf8SpanParsable<System.Net.IPNetwork>.TryParse(System.ReadOnlySpan`1<System.Byte>,System.IFormatProvider,System.Net.IPNetwork&)