Skip to content

feat(dns): look up PTR records by interface address#2711

Open
chet wants to merge 2 commits into
NVIDIA:mainfrom
chet:gh-issue-2639
Open

feat(dns): look up PTR records by interface address#2711
chet wants to merge 2 commits into
NVIDIA:mainfrom
chet:gh-issue-2639

Conversation

@chet

@chet chet commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The DB lookup behind reverse DNS: find_ptr_record(address) returns the FQDN(s) the forward shortname view publishes for whichever primary or BMC interface holds that address — its WHERE mirrors dns_records_shortname_combined, so a forward A/AAAA record and its PTR round-trip.

  • New address index on machine_interface_addresses → the lookup is an index probe, not a scan.
  • DbPtrRecord decodes the FQDN answer (a hostname, unlike the IP-valued DbResourceRecord).
  • DB-backed test: a primary interface + address resolves to its FQDN; an unheld address resolves to nothing.

The handler (#2641) will parse the incoming arpa qname with arpa_qname_to_ip (#2637) and call this; carbide-dns's PTR unlock is #2643.

Implements #2639. Part of the #2630 epic (reverse DNS / PTR records).

Stacked on #2637 (the arpa parser). Until that merges, this PR's diff also shows its commit.

Draft pending review.

Reverse DNS resolves a PTR query name -- an address in `in-addr.arpa` / `ip6.arpa` form -- back to a hostname. The lookup direction, arpa name to address, is a clean inverse, so this does it in Rust rather than a large PL/pgSQL function: `arpa_qname_to_ip` reverses the four octets (IPv4) or the 32 nibbles (IPv6) and hands back an `IpAddr`. The PTR handler will use that to look the interface up by address directly -- an indexed equality, not a per-row arpa string computed across a view.

`Ipv6Addr` does the address assembly, so the IPv6 case is a few lines instead of the manual hextet expansion the equivalent SQL needs. This is the conversion foundation for reverse DNS; the indexed address lookup (`find_ptr_record`) and the handler arm build on it.

Tests cover the IPv4 and IPv6 forms plus rejection of non-arpa and malformed names.

This supports NVIDIA#2637.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Jun 20, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e45308fe-538f-463a-9e05-f1e18dd9d50c

📥 Commits

Reviewing files that changed from the base of the PR and between 287fa02 and 0e3e818.

📒 Files selected for processing (3)
  • crates/api-db/migrations/20260620153439_machine_interface_addresses_address_index.sql
  • crates/api-db/src/dns/mod.rs
  • crates/api-db/src/dns/resource_record.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/api-db/migrations/20260620153439_machine_interface_addresses_address_index.sql
  • crates/api-db/src/dns/mod.rs
  • crates/api-db/src/dns/resource_record.rs

Summary by CodeRabbit

  • New Features

    • Added reverse DNS (PTR) record lookup functionality to resolve IP addresses to their associated hostnames
    • Supports PTR queries for both primary network interfaces and Baseboard Management Controller (BMC) interfaces with configurable TTL values
    • Includes normalization handling for reverse-DNS query name parsing
  • Performance

    • Optimized database indexing on address fields to improve reverse-DNS lookup performance

Walkthrough

A database index is added on machine_interface_addresses.address to support efficient PTR lookups. A new arpa_qname_to_ip parser converts .in-addr.arpa/.ip6.arpa query names to IpAddr. A new DbPtrRecord struct and find_ptr_record async function query the database by joining interface and domain tables to resolve PTR targets.

Changes

PTR Record Database Layer

Layer / File(s) Summary
Address index migration and DbPtrRecord model
crates/api-db/migrations/20260620153439_machine_interface_addresses_address_index.sql, crates/api-db/src/dns/resource_record.rs
Creates machine_interface_addresses_address_idx on the address column and defines the DbPtrRecord struct with a FromRow implementation mapping ptr_content, ttl, and domain_id from SQL rows.
ARPA name parser and find_ptr_record query
crates/api-db/src/dns/mod.rs, crates/api-db/src/dns/resource_record.rs
Adds arpa_qname_to_ip to normalize and parse IPv4/IPv6 reverse-DNS PTR names, and adds find_ptr_record which joins machine_interface_addressesmachine_interfacesdomains, applies COALESCE-based TTL defaulting from dns_record_metadata, and filters by address cast to inet.
Unit and integration tests
crates/api-db/src/dns/mod.rs
Adds unit tests for arpa_qname_to_ip (IPv4/IPv6 paths, malformed rejection, case normalization) and a SQLx integration test seeding DNS tables to assert find_ptr_record resolves the expected PTR FQDN and returns empty results for unknown addresses.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #2637: This PR implements the exact solution specified in the issue: arpa_qname_to_ip function for reverse-DNS PTR query name parsing, the address index migration for efficient lookup, and find_ptr_record query function.
  • #2639: This PR delivers the DbPtrRecord struct and find_ptr_record function requested for IP-to-PTR record resolution via machine interfaces and domains.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the primary change: implementing PTR record lookup functionality by interface address, which aligns with the changeset's core objective.
Description check ✅ Passed The description comprehensively explains the feature, its rationale, performance optimization, and integration points, directly relating to all material changes in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@chet

chet commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
crates/api-db/src/dns/mod.rs (1)

275-337: ⚡ Quick win

Expand PTR integration coverage to include the BMC branch and table the inputs.

This test validates a primary-interface hit and an unknown miss, but it does not exercise the interface_type = 'Bmc' branch used by find_ptr_record. Please add a BMC case (and ideally a non-primary/non-BMC negative case) and run these as table cases for the same operation.

As per coding guidelines, “Reach for a table whenever two or more tests call the same operation with different inputs.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/api-db/src/dns/mod.rs` around lines 275 - 337, The test
find_ptr_record_resolves_address_to_hostname currently validates only primary
interface and unknown address cases but does not cover the BMC interface branch.
Refactor this test into a table-driven test structure with multiple cases that
exercise the same super::resource_record::find_ptr_record function with
different inputs: add setup steps for a BMC interface case, a
non-primary/non-BMC negative case, while keeping the existing primary interface
and unknown address cases, then iterate through each case calling
find_ptr_record with different addresses and validating the expected outcomes.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@crates/api-db/migrations/20260620153439_machine_interface_addresses_address_index.sql`:
- Around line 4-5: The CREATE INDEX statement for
machine_interface_addresses_address_idx is using a blocking index build strategy
that can lock writes on the machine_interface_addresses table during migration.
Modify the index creation to use the CONCURRENTLY keyword to perform a
non-blocking concurrent build, and ensure this migration follows the repo's
non-transaction migration pattern (typically by removing any transaction
wrapping) to allow online index creation without blocking production writes.

In `@crates/api-db/src/dns/resource_record.rs`:
- Around line 133-152: The comment for the find_ptr_record function overstates
parity with dns_records_shortname_combined by claiming the WHERE clause mirrors
"exactly". Update the comment to clarify that while the WHERE clause does match
dns_records_shortname_combined exactly, the JOIN structure intentionally differs
by omitting the LEFT JOIN dns_record_types (since the function does not select
q_type), and the TTL handling intentionally diverges by using COALESCE(meta.ttl,
300) with a hardcoded default of 300 instead of allowing NULL values like the
view does. Also document the rationale for choosing 300 as the TTL default to
aid future maintainers.

---

Nitpick comments:
In `@crates/api-db/src/dns/mod.rs`:
- Around line 275-337: The test find_ptr_record_resolves_address_to_hostname
currently validates only primary interface and unknown address cases but does
not cover the BMC interface branch. Refactor this test into a table-driven test
structure with multiple cases that exercise the same
super::resource_record::find_ptr_record function with different inputs: add
setup steps for a BMC interface case, a non-primary/non-BMC negative case, while
keeping the existing primary interface and unknown address cases, then iterate
through each case calling find_ptr_record with different addresses and
validating the expected outcomes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 4b09d203-b307-4774-9f65-fb31bcf582cd

📥 Commits

Reviewing files that changed from the base of the PR and between 79e54c0 and 287fa02.

📒 Files selected for processing (3)
  • crates/api-db/migrations/20260620153439_machine_interface_addresses_address_index.sql
  • crates/api-db/src/dns/mod.rs
  • crates/api-db/src/dns/resource_record.rs

Comment thread crates/api-db/src/dns/resource_record.rs
With reverse-DNS resolution parsing a PTR query name into an address (NVIDIA#2637), this is the lookup that answers it: `find_ptr_record(address)` returns the FQDN(s) the forward shortname view publishes for whichever primary or BMC interface holds that address. The `WHERE` mirrors `dns_records_shortname_combined` exactly, so a forward A/AAAA record and its PTR round-trip.

The query filters on `address`, and a new index on `machine_interface_addresses(address)` makes it an index probe rather than a scan. `DbPtrRecord` decodes the FQDN answer -- a hostname, unlike the IP-valued `DbResourceRecord`.

A DB-backed test inserts a primary interface and its address, asserts it resolves to the right FQDN, and asserts an address no interface holds resolves to nothing.

This supports NVIDIA#2639.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
@chet

chet commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai Thanks for the eyes. Updated -- the find_ptr_record test now inserts a primary, a BMC, and a plain data interface and tables the lookups (primary + BMC resolve; the non-primary/non-BMC interface and an address no interface holds resolve to nothing), covering the interface_type = 'Bmc' branch. PTAL. Resolve if good, thanks!

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat crates/api-db/src/dns/mod.rs
echo "---FILE BOUNDARY---"
cat crates/api-db/src/dns/resource_record.rs

Length of output: 20649



@chet The updated test is clean and the coverage is complete. All four cases are verified:

Address Interface Expectation Filter branch hit
192.168.0.1 primary resolves → host-1.dwrt1.com. primary_interface = TRUE
192.168.0.2 BMC resolves → host-1-bmc.dwrt1.com. interface_type = 'Bmc'
192.168.0.3 plain data → empty neither branch
10.9.9.9 none → empty no row at all

The SQL predicate, FQDN construction (concat(hostname, '.', domain, '.')), TTL coalescing, and index usage all look correct. Resolving.

[resolve]

@chet chet marked this pull request as ready for review June 21, 2026 04:22
@chet chet requested a review from a team as a code owner June 21, 2026 04:22
@github-actions

Copy link
Copy Markdown

🔍 Container Scan Summary

Service Total Critical High Medium Low Other
boot-artifacts-aarch64 3 0 0 3 0 0
boot-artifacts-x86_64 3 0 0 3 0 0
forge-admin-cli-x86_64 264 6 23 99 6 130
machine-validation-runner 704 34 183 258 35 194
machine_validation 704 34 183 258 35 194
nvmetal-carbide 704 34 183 258 35 194
TOTAL 2382 108 572 879 111 712

Per-CVE detail lives in the per-service grype-* artifacts (JSON + SARIF). Severity counts only — no CVE IDs published here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant