public void IPv6Formats(string address) { Assert.True(IpAddressV6.TryParse(address, out _)); }
internal static bool TryParse(ref Tokenizer tokenizer, out IpAddressV6 result) { // Shortest IPv6 is 2 chars (::) // Longest regular IPv6 is 39 chars (0000:0000:0000:0000:0000:0000:0000:0000) // Longest IPv4 mapped IPv6 is 45 chars (0000:0000:0000:0000:0000:ffff:255.255.255.255) if (tokenizer.Length < 2 || tokenizer.Length > 45) { result = default; return(false); } ulong high = 0; ulong low = 0; 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, therefore an IPv4 mapped IPv6 ParsedToken slashTkn = tokenizer.PeekReverse(false); 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 // 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(tokenizer, 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 IpAddressV6(high, low); return(true); }
public static bool TryParse(ReadOnlySpan <char> value, out IpAddressV6 result) { Tokenizer tokenizer = new(value); return(TryParse(ref tokenizer, out result)); }
private static bool TryReadIPv4MappedIPv6(Tokenizer tokenizer, out IpAddressV6 result) { ulong high = 0; ulong low = 0; byte segmentsRead = 0; ParsedToken token; // Read reverse, we're only interested in the IPv4 on the end byte toRead = 4; for (byte i = 0; i < toRead; i++) { token = tokenizer.ParseAndAdvanceReverse(false); if (token.Type == TokenType.None) { break; } if (i > 0) { // The read token MUST be a dot if (token.Type != TokenType.Dot) { result = default; return(false); } // Advance once more token = tokenizer.ParseAndAdvanceReverse(false); } // Read a number if (token.Type == TokenType.Number) { BitUtilities.SetByte(ref low, ref high, (byte)(15 - i), token.Value); segmentsRead++; } else { result = default; return(false); } } // Assert that the next tokens are [Double-/Colon, ffff, Colon] token = tokenizer.ParseAndAdvanceReverse(false); if (token.Type != TokenType.Colon) { result = default; return(false); } token = tokenizer.ParseAndAdvanceReverse(true); if (token.Type != TokenType.Number || token.Value != 0xffff) { result = default; return(false); } token = tokenizer.ParseAndAdvanceReverse(false); if (token.Type is not TokenType.Colon && token.Type is not TokenType.DoubleColon) { result = default; return(false); } // Place 0xFFFF in the correct position BitUtilities.SetTuplet(ref low, ref high, 5, 0xFFFF); result = new IpAddressV6(high, low); return(true); }
public static bool TryParse(string value, out IpAddressV6 result) { return(TryParse(value.AsSpan(), out result)); }
public bool Contains(IpAddressV6 other) { IpAddressV6 mask = NetworkMask; return((mask & other) == NetworkAddress); }