Stage 7a: the addresses of a locator and the IP addresses of netip
lib/src/locator.dart starts with the addresses of spec 44.1, a port of
CheckURI and Address.Host of package locator of datekeys-go: the same
checks in the same order and the same texts, on the UTF-8 of the address,
with Go's %q of the first byte that RFC 3986 does not allow. And
checkResolvedIp, the check of the IP that a name resolves to, which a
reader runs on every connection: the classification of publicIP on the 4
or 16 bytes of an address, an IPv4-mapped one not public. Go has no such
function, since its reader does not download.
lib/src/ipaddr.dart, internal, parses IPv4 and IPv6 with the exact
acceptance of netip.ParseAddr, writes them as its String, and classifies
them as publicIP, with the blocks of spec 44.1. It works byte by byte, so
that the 128 bits of IPv6 stay exact on the web, and never uses dart:io.
The tests run the 247 addresses of locator.json and the 2 800 of the
vectors, the 1 700 strings of netip and the 868 byte strings of publicIP,
on the VM and on Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 days ago
|
|
|
// The addresses of a locator against Go (spec §44.1): checkAddressUri and
|
|
|
|
|
// LocatorAddress.host on the 247 addresses of testdata/vectors/locator.json
|
|
|
|
|
// and on the 2 800 of tool/locator_go_vectors.go, with the texts of
|
|
|
|
|
// locator.CheckURI and Address.Host; the parser of IP addresses against
|
|
|
|
|
// netip.ParseAddr and String; and the classification of publicIP on the
|
|
|
|
|
// bytes of addresses, as checkResolvedIp checks the IP that a name resolves
|
|
|
|
|
// to. Everything here runs on the VM and compiled to JavaScript, from the
|
|
|
|
|
// constant of locator_uris.g.dart.
|
|
|
|
|
library;
|
|
|
|
|
|
|
|
|
|
import 'package:datekeys/src/bytes.dart';
|
|
|
|
|
import 'package:datekeys/src/ipaddr.dart';
|
|
|
|
|
import 'package:datekeys/src/locator.dart';
|
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
|
|
|
|
|
|
import 'locator_support.dart';
|
|
|
|
|
import 'vectors/locator_uris.g.dart';
|
|
|
|
|
import 'wordkey_support.dart' show fromWtf8;
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
final v = decodeJson(locatorUrisJson);
|
|
|
|
|
|
|
|
|
|
void expectUri(List<Object?> c) {
|
|
|
|
|
final uri = c[0]! as String;
|
|
|
|
|
final want = textOf(v, c[1]);
|
|
|
|
|
expect(errorText(() => checkAddressUri(uri)), want, reason: uri);
|
|
|
|
|
final host = LocatorAddress(uri).host;
|
|
|
|
|
expect(host, c[2], reason: uri);
|
|
|
|
|
// As FuzzCheckURI of Go: the host shown is in the address as it is
|
|
|
|
|
// written, with no decoding.
|
|
|
|
|
if (want.isEmpty) expect(uri.contains(host) && host.isNotEmpty, isTrue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
group('checkAddressUri', () {
|
|
|
|
|
test('the addresses of locator.json, with the texts of Go', () {
|
|
|
|
|
final cases = rows(v, 'testdata');
|
|
|
|
|
expect(cases, hasLength(247));
|
|
|
|
|
cases.forEach(expectUri);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('the addresses at the edges of §44.1 and drawn from a seed', () {
|
|
|
|
|
final cases = rows(v, 'uris');
|
|
|
|
|
expect(cases, hasLength(2800));
|
|
|
|
|
cases.forEach(expectUri);
|
|
|
|
|
// Every rule rejects some and some pass.
|
|
|
|
|
final texts = {for (final c in cases) c[1]};
|
|
|
|
|
expect(texts.length, greaterThan(20));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('an address that is not ASCII is measured and quoted in UTF-8', () {
|
|
|
|
|
// Go counts the bytes of the string and quotes its first byte that is
|
|
|
|
|
// not of RFC 3986 as a rune, 0xc3 as U+00C3 in quotes.
|
|
|
|
|
final c = rows(
|
|
|
|
|
v,
|
|
|
|
|
'uris',
|
|
|
|
|
).firstWhere((c) => c[0] == 'https://ejemplo.org/\u00e9');
|
|
|
|
|
expectUri(c);
|
|
|
|
|
expect(textOf(v, c[1]), contains("'\u00c3'"));
|
|
|
|
|
// A lone surrogate is three bytes that are not UTF-8, as Go reads
|
|
|
|
|
// those bytes.
|
|
|
|
|
for (final c in rows(v, 'wtf8')) {
|
|
|
|
|
final uri = fromWtf8(fromHex(c[0]! as String))!;
|
|
|
|
|
expect(errorText(() => checkAddressUri(uri)), textOf(v, c[1]));
|
|
|
|
|
expect(LocatorAddress(uri).host, '');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('a doubled opening bracket and a CID with one more character whose '
|
|
|
|
|
'bits are zero are refused, as by Go since the draft v0.13', () {
|
|
|
|
|
// checkHost of Go takes one pair of brackets, and isCIDv1 refuses 5 or
|
|
|
|
|
// more bits left over: neither is the form that spec §44.1 asks for.
|
Stage 7a: the addresses of a locator and the IP addresses of netip
lib/src/locator.dart starts with the addresses of spec 44.1, a port of
CheckURI and Address.Host of package locator of datekeys-go: the same
checks in the same order and the same texts, on the UTF-8 of the address,
with Go's %q of the first byte that RFC 3986 does not allow. And
checkResolvedIp, the check of the IP that a name resolves to, which a
reader runs on every connection: the classification of publicIP on the 4
or 16 bytes of an address, an IPv4-mapped one not public. Go has no such
function, since its reader does not download.
lib/src/ipaddr.dart, internal, parses IPv4 and IPv6 with the exact
acceptance of netip.ParseAddr, writes them as its String, and classifies
them as publicIP, with the blocks of spec 44.1. It works byte by byte, so
that the 128 bits of IPv6 stay exact on the web, and never uses dart:io.
The tests run the 247 addresses of locator.json and the 2 800 of the
vectors, the 1 700 strings of netip and the 868 byte strings of publicIP,
on the VM and on Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 days ago
|
|
|
for (final uri in [
|
|
|
|
|
'https://[[2000::]/',
|
|
|
|
|
'ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdia',
|
|
|
|
|
]) {
|
|
|
|
|
final c = rows(v, 'uris').firstWhere((c) => c[0] == uri);
|
|
|
|
|
expect(c[1], isNot(0), reason: uri);
|
Stage 7a: the addresses of a locator and the IP addresses of netip
lib/src/locator.dart starts with the addresses of spec 44.1, a port of
CheckURI and Address.Host of package locator of datekeys-go: the same
checks in the same order and the same texts, on the UTF-8 of the address,
with Go's %q of the first byte that RFC 3986 does not allow. And
checkResolvedIp, the check of the IP that a name resolves to, which a
reader runs on every connection: the classification of publicIP on the 4
or 16 bytes of an address, an IPv4-mapped one not public. Go has no such
function, since its reader does not download.
lib/src/ipaddr.dart, internal, parses IPv4 and IPv6 with the exact
acceptance of netip.ParseAddr, writes them as its String, and classifies
them as publicIP, with the blocks of spec 44.1. It works byte by byte, so
that the 128 bits of IPv6 stay exact on the web, and never uses dart:io.
The tests run the 247 addresses of locator.json and the 2 800 of the
vectors, the 1 700 strings of netip and the 868 byte strings of publicIP,
on the VM and on Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 days ago
|
|
|
expectUri(c);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
group('IP addresses', () {
|
|
|
|
|
test('parseIpAddress accepts what netip.ParseAddr accepts', () {
|
|
|
|
|
final cases = rows(v, 'ips');
|
|
|
|
|
expect(cases.length, greaterThan(1500));
|
|
|
|
|
for (final c in cases) {
|
|
|
|
|
final s = c[0]! as String;
|
|
|
|
|
final a = parseIpAddress(s);
|
|
|
|
|
if (c[1] == false) {
|
|
|
|
|
expect(a, isNull, reason: s);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
expect(a, isNotNull, reason: s);
|
|
|
|
|
expect(toHex(a!.bytes), c[2], reason: s);
|
|
|
|
|
expect(a.zone, c[3], reason: s);
|
|
|
|
|
expect('$a', c[4], reason: s);
|
|
|
|
|
expect(isPublicIp(a), c[5], reason: s);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test(
|
|
|
|
|
'checkResolvedIp checks the bytes of an address as publicIP, but NAT64',
|
|
|
|
|
() {
|
|
|
|
|
for (final c in rows(v, 'public')) {
|
|
|
|
|
final b = fromHex(c[0]! as String);
|
|
|
|
|
if (c[1] == null) {
|
|
|
|
|
expect(() => checkResolvedIp(b), throwsArgumentError);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
final a = IpAddress.fromBytes(b);
|
|
|
|
|
expect('$a', c[2]);
|
|
|
|
|
expect(isPublicIp(a), c[1], reason: '$a');
|
|
|
|
|
// An address of NAT64 counts by the IPv4 address it holds (spec
|
|
|
|
|
// v0.13): resolved_ip_test.dart runs those.
|
|
|
|
|
if (b.length == 16 &&
|
|
|
|
|
toHex(b.sublist(0, 12)) == '0064ff9b0000000000000000') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
expect(
|
|
|
|
|
errorText(() => checkResolvedIp(b)),
|
|
|
|
|
c[1] == true
|
|
|
|
|
? ''
|
|
|
|
|
: 'locator: an https address whose name resolves to ${c[2]}, '
|
|
|
|
|
'an IP address that is not public',
|
|
|
|
|
);
|
Stage 7a: the addresses of a locator and the IP addresses of netip
lib/src/locator.dart starts with the addresses of spec 44.1, a port of
CheckURI and Address.Host of package locator of datekeys-go: the same
checks in the same order and the same texts, on the UTF-8 of the address,
with Go's %q of the first byte that RFC 3986 does not allow. And
checkResolvedIp, the check of the IP that a name resolves to, which a
reader runs on every connection: the classification of publicIP on the 4
or 16 bytes of an address, an IPv4-mapped one not public. Go has no such
function, since its reader does not download.
lib/src/ipaddr.dart, internal, parses IPv4 and IPv6 with the exact
acceptance of netip.ParseAddr, writes them as its String, and classifies
them as publicIP, with the blocks of spec 44.1. It works byte by byte, so
that the 128 bits of IPv6 stay exact on the web, and never uses dart:io.
The tests run the 247 addresses of locator.json and the 2 800 of the
vectors, the 1 700 strings of netip and the 868 byte strings of publicIP,
on the VM and on Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 days ago
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
Stage 7a: the addresses of a locator and the IP addresses of netip
lib/src/locator.dart starts with the addresses of spec 44.1, a port of
CheckURI and Address.Host of package locator of datekeys-go: the same
checks in the same order and the same texts, on the UTF-8 of the address,
with Go's %q of the first byte that RFC 3986 does not allow. And
checkResolvedIp, the check of the IP that a name resolves to, which a
reader runs on every connection: the classification of publicIP on the 4
or 16 bytes of an address, an IPv4-mapped one not public. Go has no such
function, since its reader does not download.
lib/src/ipaddr.dart, internal, parses IPv4 and IPv6 with the exact
acceptance of netip.ParseAddr, writes them as its String, and classifies
them as publicIP, with the blocks of spec 44.1. It works byte by byte, so
that the 128 bits of IPv6 stay exact on the web, and never uses dart:io.
The tests run the 247 addresses of locator.json and the 2 800 of the
vectors, the 1 700 strings of netip and the 868 byte strings of publicIP,
on the VM and on Node.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 days ago
|
|
|
|
|
|
|
|
test('an IPv4 address mapped in IPv6 is not public', () {
|
|
|
|
|
expect(
|
|
|
|
|
() => checkResolvedIp([...List.filled(10, 0), 0xff, 0xff, 8, 8, 8, 8]),
|
|
|
|
|
throwsA(isA<LocatorException>()),
|
|
|
|
|
);
|
|
|
|
|
checkResolvedIp([8, 8, 8, 8]);
|
|
|
|
|
expect(() => checkResolvedIp([8, 8, 8, 256]), throwsArgumentError);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|