mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
fix(stalker): treat every reserved localhost name as portal-local
Codex P2 on #1364, the fourth in this class. RFC 6761 §6.3 reserves `localhost` AND every name ending in `.localhost` for the loopback interface, and resolvers honour it — so `http://stream.localhost/ch/1234_` reached the player's own machine instead of being sent to the portal to resolve. Closed the class rather than adding one more name: the suffix is matched, and `localhost.localdomain` goes in with it as the conventional `/etc/hosts` alias for 127.0.0.1 on most Linux systems. Together with the earlier rounds the predicate now covers `localhost` and `*.localhost`, `localhost.localdomain`, `127.0.0.0/8`, `0.0.0.0`, `::1`, `::`, the IPv4-mapped forms `URL` rewrites to hex, and a terminal DNS root dot on any of them. Only the suffix is reserved, so the guard must not over-match: tests pin that `localhost.cdn.example` and `notlocalhost` remain ordinary routable names and keep playing statically. Mutation-checked: dropping the suffix rule and the localdomain alias fails four tests and nothing else. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
259be083c7
commit
6114c2d459
3 files changed
+51
-3
No files matched your search
@@ -333,7 +333,7 @@ path, so they cannot regress a portal that works today:
|
||||
| A row carrying neither flag KEY | `create_link` | Absence means "no evidence", not "no". A stock portal returns both flags on every row, so their PRESENCE is the provenance signal — and the only one available, because rows persisted into Favorites/Recently Viewed before this change were stripped of them by `buildStalkerSelectedVodItem`'s whitelist, making a legacy snapshot indistinguishable from a genuinely unflagged row. There is no migration for those. **Radio is the documented exception**: a directly playable radio command has always played as-is, so a flagless radio row keeps that rather than newly minting. |
|
||||
| Relative `/media/file_12.mpg` or query-only `?token=…` | `create_link` | Only the portal turns those into an address; the VOD `has_files` rewrite produces exactly the first shape. |
|
||||
| Non-HTTP scheme (`ffrt4://ch/live/…`) | `create_link` | Portal-internal pseudo-URL. |
|
||||
| Portal-local host — `localhost`, all of `127.0.0.0/8`, `0.0.0.0`, `::1`, `::`, and the IPv4-mapped forms `URL` normalizes to hex (`::ffff:7f00:1`) | `create_link` | `ffrt3 http://localhost/ch/1234_` is an instruction to the portal, not an address a set-top box could open. |
|
||||
| Portal-local host — `localhost` and any `*.localhost` name (RFC 6761 §6.3 reserves the whole suffix for loopback), `localhost.localdomain`, all of `127.0.0.0/8`, `0.0.0.0`, `::1`, `::`, and the IPv4-mapped forms `URL` normalizes to hex (`::ffff:7f00:1`); a terminal DNS root dot is stripped first | `create_link` | `ffrt3 http://localhost/ch/1234_` is an instruction to the portal, not an address a set-top box could open. |
|
||||
| Otherwise | static `cmd` | Solution prefix stripped by `normalizeStalkerPlaybackCommand()`. |
|
||||
|
||||
`fetchStalkerPlaybackLink()` applies the verdict for ITV, VOD and radio, and
|
||||
|
||||
+30
@@ -219,6 +219,36 @@ describe('stalker-link-semantics', () => {
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it.each([
|
||||
// RFC 6761 §6.3 reserves the whole `.localhost` suffix.
|
||||
['http://stream.localhost/ch/1234_'],
|
||||
['ffrt3 http://a.b.localhost/ch/1234_'],
|
||||
['http://stream.localhost./ch/1234_'],
|
||||
// Conventional /etc/hosts alias for 127.0.0.1.
|
||||
['http://localhost.localdomain/ch/1234_'],
|
||||
])('treats the localhost name %p as portal-local', (cmd) => {
|
||||
expect(
|
||||
resolveStalkerStaticPlaybackUrl({ use_http_tmp_link: '0' }, cmd)
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('does not mistake a localhost-prefixed host for a localhost name', () => {
|
||||
// Only the SUFFIX is reserved — `localhost.cdn.example` is an
|
||||
// ordinary routable name and must keep playing statically.
|
||||
expect(
|
||||
resolveStalkerStaticPlaybackUrl(
|
||||
{ use_http_tmp_link: '0' },
|
||||
'http://localhost.cdn.example/live/42.m3u8'
|
||||
)
|
||||
).toBe('http://localhost.cdn.example/live/42.m3u8');
|
||||
expect(
|
||||
resolveStalkerStaticPlaybackUrl(
|
||||
{ use_http_tmp_link: '0' },
|
||||
'http://notlocalhost/live/42.m3u8'
|
||||
)
|
||||
).toBe('http://notlocalhost/live/42.m3u8');
|
||||
});
|
||||
|
||||
it('does not mistake a non-loopback 127-lookalike for loopback', () => {
|
||||
expect(
|
||||
resolveStalkerStaticPlaybackUrl(
|
||||
|
||||
+20
-2
@@ -26,7 +26,21 @@ export interface StalkerLinkFlagSource {
|
||||
* `ffrt3 http://localhost/ch/1234_` is an instruction to the portal, never an
|
||||
* address the set-top box could open, so it always needs resolving.
|
||||
*/
|
||||
const PORTAL_LOCAL_HOSTNAMES = new Set(['localhost', '0.0.0.0', '::1', '::']);
|
||||
const PORTAL_LOCAL_HOSTNAMES = new Set([
|
||||
'localhost',
|
||||
// Conventional `/etc/hosts` alias for 127.0.0.1 on most Linux systems.
|
||||
'localhost.localdomain',
|
||||
'0.0.0.0',
|
||||
'::1',
|
||||
'::',
|
||||
]);
|
||||
|
||||
/**
|
||||
* RFC 6761 §6.3 reserves `localhost` AND every name ending in `.localhost`
|
||||
* for the loopback interface, and resolvers honour it — so `stream.localhost`
|
||||
* would reach the player's own machine.
|
||||
*/
|
||||
const LOCALHOST_SUFFIX = '.localhost';
|
||||
|
||||
/** IPv4 reserves all of `127.0.0.0/8` for loopback, not just `127.0.0.1`. */
|
||||
const IPV4_LOOPBACK = /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
||||
@@ -49,7 +63,11 @@ function isPortalLocalHostname(hostname: string): boolean {
|
||||
// exact-name check has to strip it or `http://localhost./ch/1_` reads as a
|
||||
// remote host.
|
||||
const host = hostname.replace(/^\[|\]$/g, '').replace(/\.$/, '');
|
||||
if (PORTAL_LOCAL_HOSTNAMES.has(host) || IPV4_LOOPBACK.test(host)) {
|
||||
if (
|
||||
PORTAL_LOCAL_HOSTNAMES.has(host) ||
|
||||
host.endsWith(LOCALHOST_SUFFIX) ||
|
||||
IPV4_LOOPBACK.test(host)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user