fix(p2p-stats): show peer flags for DNS6 relay hostnames

Extract embedded IPv6 addresses from dns6 multiaddrs so geo lookup and country flags work for relay peers that publish IPv6 via DNS hostnames.
This commit is contained in:
Tommaso Casaburi
2026-05-31 18:02:20 +07:00
parent 872116b132
commit 3480dc0505
3 changed files with 110 additions and 1 deletions
+32
View File
@@ -37,6 +37,13 @@ describe('extractIpv6FromAddress', () => {
expect(extractIpv6FromAddress('/ip6/2001:4860:4860::8888/tcp/4001/ws')).toBe('2001:4860:4860::8888');
expect(extractIpFromAddress('/ip6/2001:4860:4860::8888/tcp/4001/ws')).toBe('2001:4860:4860::8888');
});
it('extracts an IPv6 embedded with dashes in a DNS hostname', () => {
const address = '/dns6/2a11-6100-0-5e9f--0.k51qzi5uqu5djg5pdoi9a98.example/tcp/443/wss/p2p/12D3KooWExample';
expect(extractIpv6FromAddress(address)).toBe('2a11:6100:0:5e9f::0');
expect(extractIpFromAddress(address)).toBe('2a11:6100:0:5e9f::0');
});
});
describe('isPrivateOrReservedIpv4', () => {
@@ -261,6 +268,31 @@ describe('fetchPeerMapLocation', () => {
vi.unstubAllGlobals();
});
it('resolves a peer location from a DNS6 hostname with an embedded IPv6', async () => {
const fetchMock = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({
cityName: 'Reykjavik',
countryCode: 'IS',
ipAddress: '2a11:6100:0:5e9f::0',
latitude: 64.1466,
longitude: -21.9426,
}),
});
vi.stubGlobal('fetch', fetchMock);
await expect(fetchPeerMapLocation('/dns6/2a11-6100-0-5e9f--0.k51qzi5uqu5djg5pdoi9a98.example/tcp/443/wss')).resolves.toMatchObject({
countryCode: 'is',
label: 'Reykjavik, IS',
lat: 64.1466,
lon: -21.9426,
source: 'geoip',
});
expect(fetchMock.mock.calls[0][0]).toBe('https://free.freeipapi.com/api/json/2a11%3A6100%3A0%3A5e9f%3A%3A0');
vi.unstubAllGlobals();
});
});
describe('getApproximateCountryCode', () => {
+11 -1
View File
@@ -284,7 +284,17 @@ export const extractIpv4FromAddress = (address: string): string | null => {
export const extractIpv6FromAddress = (address: string): string | null => {
const direct = /\/ip6\/([^/]+)/.exec(address);
return direct ? direct[1] : null;
if (direct) return direct[1];
const dns = /\/dns6\/([^/]+)/i.exec(address);
const firstLabel = dns?.[1]?.split('.')[0];
if (!firstLabel || !firstLabel.includes('-') || !/^[0-9a-f-]+$/i.test(firstLabel)) return null;
const candidate = firstLabel.replaceAll('-', ':').toLowerCase();
try {
new URL(`http://[${candidate}]/`);
return candidate;
} catch {
return null;
}
};
const parseOctets = (ip: string): number[] | null => {