< Summary

Line coverage
65%
Covered lines: 128
Uncovered lines: 66
Coverable lines: 194
Total lines: 394
Line coverage: 65.9%
Branch coverage
54%
Covered branches: 64
Total branches: 117
Branch coverage: 54.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: ToUShort(...)50%44100%
File 1: ParseHostNumber(...)0%10100%
File 1: IsValid(...)100%22100%
File 1: IsValidCanonical(...)57.14%424277.55%
File 1: ParseNonCanonical(...)65.45%555582.79%
File 2: ParseCanonicalName(...)0%440%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/Common/src/System/Net/IPv4AddressHelper.Common.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.Numerics;
 7using System.Runtime.CompilerServices;
 8
 9namespace System.Net
 10{
 11    internal static partial class IPv4AddressHelper
 12    {
 13        internal const long Invalid = -1;
 14        private const long MaxIPv4Value = uint.MaxValue; // the native parser cannot handle MaxIPv4Value, only MaxIPv4Va
 15
 16        private const int Octal = 8;
 17        private const int Decimal = 10;
 18        private const int Hex = 16;
 19
 20        private const int NumberOfLabels = 4;
 21
 22        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 23        internal static ushort ToUShort<TChar>(TChar value)
 24            where TChar : unmanaged, IBinaryInteger<TChar>
 964225        {
 964226            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 27
 964228            return typeof(TChar) == typeof(char)
 964229                ? (char)(object)value
 964230                : (byte)(object)value;
 964231        }
 32
 33        // Only called from the IPv6Helper, only parse the canonical format
 34        internal static unsafe int ParseHostNumber<TChar>(ReadOnlySpan<TChar> str, int start, int end)
 35            where TChar : unmanaged, IBinaryInteger<TChar>
 036        {
 037            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 38
 039            Span<byte> numbers = stackalloc byte[NumberOfLabels];
 40
 041            for (int i = 0; i < numbers.Length; ++i)
 042            {
 043                int b = 0;
 44                int ch;
 45
 046                for (; (start < end) && (ch = ToUShort(str[start])) != '.' && ch != ':'; ++start)
 047                {
 048                    b = (b * 10) + ch - '0';
 049                }
 50
 051                numbers[i] = (byte)b;
 052                ++start;
 053            }
 54
 055            return BinaryPrimitives.ReadInt32BigEndian(numbers);
 056        }
 57
 58        //
 59        // IsValid
 60        //
 61        //  Performs IsValid on a substring. Updates the index to where we
 62        //  believe the IPv4 address ends
 63        //
 64        // Inputs:
 65        //  <argument>  name
 66        //      string containing possible IPv4 address
 67        //
 68        //  <argument>  start
 69        //      offset in <name> to start checking for IPv4 address
 70        //
 71        //  <argument>  end
 72        //      offset in <name> of the last character we can touch in the check
 73        //
 74        // Outputs:
 75        //  <argument>  end
 76        //      index of last character in <name> we checked
 77        //
 78        //  <argument>  allowIPv6
 79        //      enables parsing IPv4 addresses embedded in IPv6 address literals
 80        //
 81        //  <argument>  notImplicitFile
 82        //      do not consider this URI holding an implicit filename
 83        //
 84        //  <argument>  unknownScheme
 85        //      the check is made on an unknown scheme (suppress IPv4 canonicalization)
 86        //
 87        // Assumes:
 88        // The address string is terminated by either
 89        // end of the string, characters ':' '/' '\' '?'
 90        //
 91        //
 92        // Returns:
 93        //  bool
 94        //
 95        // Throws:
 96        //  Nothing
 97        //
 98
 99        //Remark: MUST NOT be used unless all input indexes are verified and trusted.
 100        internal static bool IsValid<TChar>(ReadOnlySpan<TChar> name, out int end, bool allowIPv6, bool notImplicitFile,
 101            where TChar : unmanaged, IBinaryInteger<TChar>
 448102        {
 103            // IPv6 can only have canonical IPv4 embedded. Unknown schemes will not attempt parsing of non-canonical IPv
 448104            if (allowIPv6 || unknownScheme)
 202105            {
 202106                return IsValidCanonical(name, out end, allowIPv6, notImplicitFile);
 107            }
 108            else
 246109            {
 246110                return ParseNonCanonical(name, out end, notImplicitFile) != Invalid;
 111            }
 448112        }
 113
 114        //
 115        // IsValidCanonical
 116        //
 117        //  Checks if the substring is a valid canonical IPv4 address or an IPv4 address embedded in an IPv6 literal
 118        //  This is an attempt to parse ABNF productions from RFC3986, Section 3.2.2:
 119        //     IP-literal = "[" ( IPv6address / IPvFuture  ) "]"
 120        //     IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
 121        //     dec-octet   = DIGIT                 ; 0-9
 122        //                 / %x31-39 DIGIT         ; 10-99
 123        //                 / "1" 2DIGIT            ; 100-199
 124        //                 / "2" %x30-34 DIGIT     ; 200-249
 125        //                 / "25" %x30-35          ; 250-255
 126        //
 127        internal static bool IsValidCanonical<TChar>(ReadOnlySpan<TChar> name, out int end, bool allowIPv6, bool notImpl
 128            where TChar : unmanaged, IBinaryInteger<TChar>
 202129        {
 202130            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 131
 202132            end = 0; // Default value in case of failure
 202133            int dots = 0;
 202134            long number = 0;
 202135            bool haveNumber = false;
 202136            int start = 0;
 137
 1824138            while (start < name.Length)
 1824139            {
 1824140                int ch = ToUShort(name[start]);
 141
 1824142                if (allowIPv6)
 1824143                {
 144                    // For an IPv4 address nested inside an IPv6 address, the terminator is either the IPv6 address term
 1824145                    if (ch == ']' || ch == '/' || ch == '%')
 202146                    {
 202147                        break;
 148                    }
 1622149                }
 0150                else if (ch == '/' || ch == '\\' || (notImplicitFile && (ch == ':' || ch == '?' || ch == '#')))
 0151                {
 152                    // For a normal IPv4 address, the terminator is the prefix ('/' or its counterpart, '\'). If notImpl
 153                    // is one of the characters which signify the start of the rest of the URI - the port number (':'), 
 154
 0155                    break;
 156                }
 157
 158                // An explicit cast to an unsigned integer forces character values preceding '0' to underflow, eliminati
 1622159                uint parsedCharacter = (uint)(ch - '0');
 160
 1622161                if (parsedCharacter < IPv4AddressHelper.Decimal)
 1016162                {
 1016163                    if (!haveNumber && parsedCharacter == 0 &&
 1016164                        (uint)(start + 1) < (uint)name.Length && char.IsAsciiDigit((char)ToUShort(name[start + 1])))
 0165                    {
 166                        // Octal is not allowed in canonical format.
 0167                        return false;
 168                    }
 169
 1016170                    haveNumber = true;
 1016171                    number = number * IPv4AddressHelper.Decimal + parsedCharacter;
 1016172                    if (number > byte.MaxValue)
 0173                    {
 0174                        return false;
 175                    }
 1016176                }
 606177                else if (ch == '.')
 606178                {
 179                    // If the current character is not an integer, it may be the IPv4 component separator ('.')
 180
 606181                    if (!haveNumber)
 0182                    {
 0183                        return false;
 184                    }
 185
 606186                    ++dots;
 606187                    haveNumber = false;
 606188                    number = 0;
 606189                }
 190                else
 0191                {
 0192                    return false;
 193                }
 1622194                ++start;
 1622195            }
 202196            bool res = (dots == 3) && haveNumber;
 202197            end = res ? start : 0;
 202198            return res;
 202199        }
 200
 201        // Parse any canonical or noncanonical IPv4 formats and return a long between 0 and MaxIPv4Value.
 202        // Return Invalid (-1) for failures.
 203        // If the address has less than three dots, only the rightmost section is assumed to contain the combined value 
 204        // the missing sections: 0xFF00FFFF == 0xFF.0x00.0xFF.0xFF == 0xFF.0xFFFF
 205        internal static long ParseNonCanonical<TChar>(ReadOnlySpan<TChar> name, out int end, bool notImplicitFile)
 206            where TChar : unmanaged, IBinaryInteger<TChar>
 246207        {
 246208            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 209
 246210            end = 0; // Default value in case of failure
 211            int numberBase;
 246212            int ch = 0;
 246213            Span<long> parts = [0, 0, 0]; // One part per octet. Final octet doesn't have a terminator, so is stored in 
 246214            long currentValue = 0;
 246215            bool atLeastOneChar = false;
 216
 217            // Parse one dotted section at a time
 246218            int dotCount = 0; // Limit 3
 246219            int current = 0;
 220
 590221            for (; current < name.Length; current++)
 418222            {
 418223                ch = ToUShort(name[current]);
 418224                currentValue = 0;
 225
 226                // Figure out what base this section is in, default to base 10.
 227                // A number starting with zero should be interpreted in base 8 / octal
 228                // If the number starts with 0x, it should be interpreted in base 16 / hex
 418229                numberBase = IPv4AddressHelper.Decimal;
 230
 418231                if (ch == '0')
 290232                {
 290233                    current++;
 290234                    atLeastOneChar = true;
 290235                    if (current < name.Length)
 275236                    {
 275237                        ch = ToUShort(name[current]);
 238
 275239                        if (ch == 'x' || ch == 'X')
 22240                        {
 22241                            numberBase = IPv4AddressHelper.Hex;
 242
 22243                            current++;
 22244                            atLeastOneChar = false;
 22245                        }
 246                        else
 253247                        {
 253248                            numberBase = IPv4AddressHelper.Octal;
 253249                        }
 275250                    }
 290251                }
 252
 253                // Parse this section
 13766254                for (; current < name.Length; current++)
 6969255                {
 6969256                    ch = ToUShort(name[current]);
 6969257                    int digitValue = HexConverter.FromChar(ch);
 258
 6969259                    if (digitValue >= numberBase)
 295260                    {
 295261                        break; // Invalid/terminator
 262                    }
 6674263                    currentValue = (currentValue * numberBase) + digitValue;
 264
 6674265                    if (currentValue > MaxIPv4Value) // Overflow
 0266                    {
 0267                        return Invalid;
 268                    }
 269
 6674270                    atLeastOneChar = true;
 6674271                }
 272
 418273                if (current < name.Length && ch == '.')
 172274                {
 172275                    if (dotCount >= 3 // Max of 3 dots and 4 segments
 172276                        || !atLeastOneChar // No empty segments: 1...1
 172277                                           // Only the last segment can be more than 255 (if there are less than 3 dots)
 172278                        || currentValue > 0xFF)
 0279                    {
 0280                        return Invalid;
 281                    }
 172282                    parts[dotCount] = currentValue;
 172283                    dotCount++;
 172284                    atLeastOneChar = false;
 172285                    continue;
 286                }
 287                // We don't get here unless we find an invalid character or a terminator
 0288                break;
 289            }
 290
 291            // Terminators
 246292            if (!atLeastOneChar)
 0293            {
 0294                return Invalid;  // Empty trailing segment: 1.1.1.
 295            }
 246296            else if (current >= name.Length)
 123297            {
 298                // end of string, allowed
 123299                end = name.Length;
 123300            }
 123301            else if (ch == '/' || ch == '\\' || (notImplicitFile && (ch == ':' || ch == '?' || ch == '#')))
 123302            {
 303                // For a normal IPv4 address, the terminator is the prefix ('/' or its counterpart, '\'). If notImplicit
 304                // is one of the characters which signify the start of the rest of the URI - the port number (':'), quer
 123305                end = current;
 123306            }
 307            else
 0308            {
 309                // not a valid terminating character
 0310                return Invalid;
 311            }
 312
 313            // Parsed, reassemble and check for overflows in the last part. Previous parts have already been checked in 
 246314            switch (dotCount)
 315            {
 316                case 0: // 0xFFFFFFFF
 170317                    return currentValue;
 318                case 1: // 0xFF.0xFFFFFF
 20319                    Debug.Assert(parts[0] <= 0xFF);
 20320                    if (currentValue > 0xffffff)
 0321                    {
 0322                        return Invalid;
 323                    }
 20324                    return (parts[0] << 24) | currentValue;
 325                case 2: // 0xFF.0xFF.0xFFFF
 16326                    Debug.Assert(parts[0] <= 0xFF);
 16327                    Debug.Assert(parts[1] <= 0xFF);
 16328                    if (currentValue > 0xffff)
 0329                    {
 0330                        return Invalid;
 331                    }
 16332                    return (parts[0] << 24) | (parts[1] << 16) | currentValue;
 333                case 3: // 0xFF.0xFF.0xFF.0xFF
 40334                    Debug.Assert(parts[0] <= 0xFF);
 40335                    Debug.Assert(parts[1] <= 0xFF);
 40336                    Debug.Assert(parts[2] <= 0xFF);
 40337                    if (currentValue > 0xff)
 0338                    {
 0339                        return Invalid;
 340                    }
 40341                    return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | currentValue;
 342                default:
 0343                    return Invalid;
 344            }
 246345        }
 346    }
 347}
 348

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.Uri/src/System/IPv4AddressHelper.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.Diagnostics;
 5
 6namespace System.Net
 7{
 8    // The class designed as to keep minimal the working set of Uri class.
 9    // The idea is to stay with static helper methods and strings
 10    internal static partial class IPv4AddressHelper
 11    {
 12        // methods
 13        // Parse and canonicalize
 14        internal static unsafe string ParseCanonicalName(string str, int start, int end, ref bool isLoopback)
 015        {
 016            long result = ParseNonCanonical(str.AsSpan(start, end - start), out _, true);
 17
 018            Debug.Assert(result != Invalid, $"Failed to parse after already validated: {str}");
 19
 020            Span<byte> numbers =
 021                unchecked(
 022                [
 023                    (byte)(result >> 24),
 024                    (byte)(result >> 16),
 025                    (byte)(result >> 8),
 026                    (byte)(result)
 027                ]);
 28
 029            isLoopback = numbers[0] == 127;
 30
 031            Span<char> stackSpace = stackalloc char[NumberOfLabels * 3 + 3];
 032            int totalChars = 0, charsWritten;
 033            for (int i = 0; i < NumberOfLabels - 1; i++)
 034            {
 035                numbers[i].TryFormat(stackSpace.Slice(totalChars), out charsWritten);
 036                int periodPos = totalChars + charsWritten;
 037                stackSpace[periodPos] = '.';
 038                totalChars = periodPos + 1;
 039            }
 40
 041            numbers[3].TryFormat(stackSpace.Slice(totalChars), out charsWritten);
 042            return new string(stackSpace.Slice(0, totalChars + charsWritten));
 043        }
 44    }
 45}
 46