feat(ipv6): surface /64 subnet allocations with custom-host PTR flow
VirtFusion's IPv6 allocation model routes a whole /64 to the VPS rather
than exposing discrete host addresses via the API. The previous module
silently filtered these entries — the client saw v4 IPs in the rDNS
panel but no v6 at all, with no indication why, and no way to set a
PTR for a specific address they were using inside the /64.
This commit surfaces subnets as first-class entries throughout:
- IpUtil::extractIps() now returns {addresses, subnets, skipped}. The
subnets bucket carries {subnet, cidr} pairs for any v6 allocation
with cidr != 128; /128 entries continue to be treated as discrete
addresses, and genuinely malformed entries still go to skipped.
- IpUtil::ipv6InSubnet($ip, $prefix, $cidrBits) — new helper that does
binary-prefix subnet containment via inet_pton + bit masking. Used
for v6 ownership verification (see below).
- PtrManager::listPtrs() emits subnet-only rows ahead of per-IP rows,
so the client UI can render the /64 as an informational anchor with
an entry point for the custom-host flow.
- client.php::rdnsUpdate adds a second ownership-check stage: if the
submitted IP is v6 AND doesn't match any discrete address, check
whether it falls inside one of the server's allocated subnets. This
preserves "only your own IPs" while unlocking the feature.
- Client-side (module.js / module.css) renders subnet rows with a
collapsible "Add host PTR" form (IP + hostname inputs) that posts
to the same rdnsUpdate endpoint. Subnet rows get a distinct cyan
accent so they visually differ from per-host rows.
The usual guards still apply to v6 custom-host writes: forward-DNS
(FCrDNS) verification, PTR regex, per-IP rate limit, same-origin /
POST-method gates. Nothing about the security envelope changes — only
what input is accepted as "you own this IP".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -545,3 +545,32 @@
|
||||
.vf-rdns-edit { flex-direction: column; align-items: stretch; }
|
||||
.vf-rdns-msg { padding-left: 0; }
|
||||
}
|
||||
|
||||
/* Subnet-only rows (IPv6 /64 allocations). Distinct visual treatment so
|
||||
customers see "this is a subnet, not a host" without reading the badge. */
|
||||
.vf-rdns-subnet-row {
|
||||
background: rgba(23, 162, 184, 0.04);
|
||||
border-left: 3px solid #17a2b8;
|
||||
padding-left: 8px;
|
||||
}
|
||||
.vf-rdns-subnet-form {
|
||||
flex-basis: 100%;
|
||||
padding: 10px 0 0 180px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
.vf-rdns-subnet-inputs {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.vf-rdns-subnet-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.vf-rdns-subnet-form { padding-left: 0; }
|
||||
.vf-rdns-subnet-inputs { flex-direction: column; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user