parseIPv6Address method
Parse the host
as an IP version 6 (IPv6) address, returning the address
as a list of 16 bytes in network byte order (big endian).
Throws a FormatException if host
is not a valid IPv6 address
representation.
Acts on the substring from start
to end
. If end
is omitted, it
defaults ot the end of the string.
Some examples of IPv6 addresses:
- ::1
- FEDC:BA98:7654:3210:FEDC:BA98:7654:3210
- 3ffe:2a00💯7031::1
- ::FFFF:129.144.52.38
- 2010:836B:4179::836B:4179
Implementation
static List<int> parseIPv6Address(String host, [int start = 0, int end]) {
end ??= host.length;
// An IPv6 address consists of exactly 8 parts of 1-4 hex digits, separated
// by `:`'s, with the following exceptions:
//
// - One (and only one) wildcard (`::`) may be present, representing a fill
// of 0's. The IPv6 `::` is thus 16 bytes of `0`.
// - The last two parts may be replaced by an IPv4 "dotted-quad" address.
// Helper function for reporting a badly formatted IPv6 address.
void error(String msg, [position]) {
throw new FormatException('Illegal IPv6 address, $msg', host, position);
}
// Parse a hex block.
int parseHex(int start, int end) {
if (end - start > 4) {
error('an IPv6 part can only contain a maximum of 4 hex digits', start);
}
int value = int.parse(host.substring(start, end), radix: 16);
if (value < 0 || value > 0xFFFF) {
error('each part must be in the range of `0x0..0xFFFF`', start);
}
return value;
}
if (host.length < 2) error('address is too short');
List<int> parts = [];
bool wildcardSeen = false;
// Set if seeing a ".", suggesting that there is an IPv4 address.
bool seenDot = false;
int partStart = start;
// Parse all parts, except a potential last one.
for (int i = start; i < end; i++) {
int char = host.codeUnitAt(i);
if (char == _COLON) {
if (i == start) {
// If we see a `:` in the beginning, expect wildcard.
i++;
if (host.codeUnitAt(i) != _COLON) {
error('invalid start colon.', i);
}
partStart = i;
}
if (i == partStart) {
// Wildcard. We only allow one.
if (wildcardSeen) {
error('only one wildcard `::` is allowed', i);
}
wildcardSeen = true;
parts.add(-1);
} else {
// Found a single colon. Parse [partStart..i] as a hex entry.
parts.add(parseHex(partStart, i));
}
partStart = i + 1;
} else if (char == _DOT) {
seenDot = true;
}
}
if (parts.length == 0) error('too few parts');
bool atEnd = (partStart == end);
bool isLastWildcard = (parts.last == -1);
if (atEnd && !isLastWildcard) {
error('expected a part after last `:`', end);
}
if (!atEnd) {
if (!seenDot) {
parts.add(parseHex(partStart, end));
} else {
List<int> last = _parseIPv4Address(host, partStart, end);
parts.add(last[0] << 8 | last[1]);
parts.add(last[2] << 8 | last[3]);
}
}
if (wildcardSeen) {
if (parts.length > 7) {
error('an address with a wildcard must have less than 7 parts');
}
} else if (parts.length != 8) {
error('an address without a wildcard must contain exactly 8 parts');
}
List<int> bytes = new Uint8List(16);
for (int i = 0, index = 0; i < parts.length; i++) {
int value = parts[i];
if (value == -1) {
int wildCardLength = 9 - parts.length;
for (int j = 0; j < wildCardLength; j++) {
bytes[index] = 0;
bytes[index + 1] = 0;
index += 2;
}
} else {
bytes[index] = value >> 8;
bytes[index + 1] = value & 0xff;
index += 2;
}
}
return bytes;
}