internal static bool TryParse(ref Tokenizer tokenizer, out IpAddressNetworkV6 result) { // Shortest IPv6 is 2 chars (::) // Longest regular IPv6 is 43 chars (0000:0000:0000:0000:0000:0000:0000:0000/128) // Longest IPv4 mapped IPv6 is 49 chars (0000:0000:0000:0000:0000:ffff:255.255.255.255/128) if (tokenizer.Length < 2 || tokenizer.Length > 49) { result = default; return(false); } ulong high = 0; ulong low = 0; byte cidr = 128; byte segmentsRead = 0; bool doReverse = false; // First pass, try reading a mask from the end of the input ParsedToken tkn = tokenizer.PeekReverse(false); if (tkn.Type == TokenType.Number) { // Could be a number, therefor a CIDR range or IPv4 ParsedToken slashTkn = tokenizer.PeekReverse(false); if (slashTkn.Type == TokenType.Slash && tkn.Value <= 128) { cidr = (byte)tkn.Value; tokenizer.AdoptPeekOffsets(); } else if (slashTkn.Type == TokenType.Dot) { // This could be an IPv4 mapped in IPv6 // Carry on, see where it gets us tokenizer.ResetPeekOffsets(); } else if (slashTkn.Type != TokenType.Number && slashTkn.Type != TokenType.Colon && slashTkn.Type != TokenType.DoubleColon && slashTkn.Type != TokenType.None) { // Any IPv6 should end on a number or double-colon (excluding the cidr mask), and the cidr mask should be 128 or lower // Single-token IPv6's are allowed, so we check for None as well result = default; return(false); } } // Test if this could be an IPv4 mapped IPv6 // This could be the case if the last two tokens are [Dot, Number] // Like '::ffff:192.168.1.0' tkn = tokenizer.PeekReverse(false); if (tkn.Type == TokenType.Number) { // If the next-to-last is a Dot, pass it on ParsedToken tmpTkn = tokenizer.PeekReverse(false); tokenizer.ResetPeekOffsets(); if (tmpTkn.Type == TokenType.Dot) { return(TryReadIPv4MappedIPv6(ref tokenizer, cidr, out result)); } } tokenizer.ResetPeekOffsets(); // Read up till a double-colon, eof or slash for (byte i = 0; i < 8; i++) { tkn = tokenizer.ParseAndAdvance(true); if (tkn.Type == TokenType.None) { break; } if (i > 0) { // The read token MUST be a colon or a double-colon if (tkn.Type == TokenType.Colon) { // Advance once more tkn = tokenizer.ParseAndAdvance(true); } else if (tkn.Type != TokenType.DoubleColon) { result = default; return(false); } } // Read a number or double-colon if (tkn.Type == TokenType.Number) { BitUtilities.SetTuplet(ref low, ref high, i, tkn.Value); segmentsRead++; } else if (tkn.Type == TokenType.DoubleColon) { doReverse = true; break; } else if (tkn.Type != TokenType.DoubleColon) { result = default; return(false); } } // Read reverse if (doReverse) { byte toRead = (byte)(8 - segmentsRead); for (byte i = 0; i < toRead; i++) { tkn = tokenizer.ParseAndAdvanceReverse(true); if (tkn.Type == TokenType.None) { break; } if (i > 0) { // The read token MUST be a colon if (tkn.Type != TokenType.Colon) { result = default; return(false); } // Advance once more tkn = tokenizer.ParseAndAdvanceReverse(true); } // Read a number if (tkn.Type == TokenType.Number) { BitUtilities.SetTuplet(ref low, ref high, (byte)(7 - i), tkn.Value); segmentsRead++; } else { result = default; return(false); } } } result = new IpAddressNetworkV6(high, low, cidr); return(true); }