Skip to content

[Enhancement] Expand hostname resolution — wire in nDPI SNI/Host, TLS cert subject, LLMNR #511

Description

@NotYuSheng

Description

Hostname resolution already exists and covers most of the obvious protocols — this issue is about closing the remaining gaps and, more importantly, connecting hostname data we already extract but throw away.

HostnameResolverService runs one read-only tshark pass and resolves IP → hostname from DHCP, mDNS, NBNS, and reverse DNS (PTR), with a priority model resolving conflicts:

SOURCE_PRIORITY = Map.of(MANUAL, 0, DHCP, 1, MDNS, 2, NBNS, 3, REVERSE_DNS, 4);
// Lower value = more authoritative for a host's own identity; the lowest wins per IP.

It's already surfaced (HostnameSourceBadge in NodeDetails and TopHostsTable) and already usable as a node label (buildNodeLines, case 'hostname'). So the feature exists; the ask is to extend it.


Gap 1 — nDPI already extracts Hostname/SNI per conversation, and it never reaches the resolver

NdpiService parses a hostname/SNI field out of every flow:

/** Matches the hostname/SNI field, e.g. [Hostname/SNI: zoom.us] → group(1) = "zoom.us". */
private static final Pattern HOSTNAME = ...
if (data.hostname() != null) conv.setHostname(data.hostname());   // NdpiService:129

That lands on ConversationEntity.hostname and is used only for the web-server detail panel (NetworkIntelligenceService:606 reads it as sniNames). It is never offered to HostnameResolverService.

This is the single biggest win available: the data is already extracted, already persisted, and already per-IP-addressable — it just isn't wired into hostname resolution. No new tshark pass, no new parsing.

Important caveat — direction matters. TLS SNI and HTTP Host are what a client asked for, i.e. the name of the destination. They are not the host's own identity the way DHCP/NBNS/mDNS names are. Naively mapping conv.hostnamesrcIp would be badly wrong. It must attach to dstIp, and even then it's the requested name (possibly a CDN, a vhost, or one of many names on one IP) rather than the machine's name. This is exactly why the existing SOURCE_PRIORITY model matters: these deserve a low-priority source, below DHCP/mDNS/NBNS, so they only fill gaps and never override a self-declared identity.

Gap 2 — protocols not currently covered

  • HTTP Host header — cleartext HTTP only, same directional caveat as SNI. WebServerLogExtractor already runs an HTTP tshark pass and already reads http.server; adding http.host there is cheap.
  • TLS SNI directly from tshark (tls.handshake.extensions_server_name) — an alternative to relying on nDPI's parse; also gives the ClientHello side explicitly.
  • TLS certificate CN/SANConversationEntity.tlsSubject is already persisted and contains the server's certificate subject. That's a server-asserted identity for the destination IP, and arguably higher quality than SNI (the server proves it, rather than the client requesting it). Also already extracted and unused for naming.
  • LLMNR — the natural companion to NBNS/mDNS on Windows networks; conspicuously absent from the current filter.
  • DHCPv6 / FQDN option, Kerberos, SMB session setup, SIP — lower value, worth a look.

Gap 3 — resolution is per-file

resolve(File pcapFile) runs per capture. A host that DHCP-named itself in snapshot 1 but is silent in snapshot 5 has no name in snapshot 5's diagram, even though we learned it. Monitor mode has ordered snapshots and IpMacObservationEntity already links IP↔MAC across files — a hostname learned once could carry forward, ideally keyed by MAC rather than IP (IPs churn under DHCP; see #510).


Acceptance Criteria

  1. Hostnames already extracted by nDPI (ConversationEntity.hostname) contribute to IP → hostname resolution, attached to the correct (destination) IP.
  2. Client-requested names (SNI / HTTP Host) rank below self-declared names (DHCP/mDNS/NBNS) in SOURCE_PRIORITY, so they only fill gaps.
  3. TLS certificate subject (CN/SAN) is evaluated as a naming source for server IPs.
  4. LLMNR is added to the resolver's tshark filter alongside mDNS/NBNS.
  5. Each new source has its own SOURCE_* constant and HostnameSourceBadge treatment, so users can see how a name was derived and judge it.
  6. A host with multiple candidate names resolves deterministically via the existing priority model.
  7. No regression to the single-pass design — HostnameResolverService must stay one tshark pass and never throw.

Design questions

  1. Is a requested name an acceptable label at all? Labelling 104.16.x.x as zoom.us is useful for triage but is not that machine's identity — one IP may serve hundreds of names. Do we label the node, or only list candidate names in NodeDetails? This is the core judgement call; getting it wrong makes the diagram confidently misleading, which is worse than a blank label.
  2. One name or many? hostname is a single column today. SNI naturally yields many names per server IP (NetworkIntelligenceService already collects sniNames as a list). Does the model need IP → set of names with sources, with one chosen for display?
  3. Where does SNI-derived naming belong — extend HostnameResolverService (needs conversation data it doesn't currently see; it takes a File), or a post-pass in AnalysisService after nDPI has run? The latter is likely simpler: deviceOverrides/hostnames are already threaded together there (AnalysisService:145-163).
  4. Carry-forward across snapshots — in scope here, or a Monitor follow-on? Keying by MAC ties it to [Enhancement] Monitor: timeline-wide network diagram with inactive nodes greyed out and MAC-grouped DHCP clients #510's MAC-grouping work.
  5. Public vs. private. For external IPs, an SNI/cert name is often the most useful label (api.stripe.com beats 34.x.x.x). For internal hosts it's usually noise. Should the source priority differ by whether the IP is private?
  6. Trust. A malicious host can DHCP-declare any name it likes. Do we surface conflicts (two IPs claiming the same name; a name that contradicts the cert) as a finding rather than silently picking a winner? That's arguably a security signal, not just a labelling detail.

Notes

Suggested sequencing:

  1. Wire nDPI's existing hostname into resolution as a low-priority, destination-attached source — highest value per line of code; data is already there.
  2. TLS cert subject as a naming source — also already persisted (tlsSubject).
  3. LLMNR + http.host — small additions to existing tshark passes.
  4. Cross-snapshot carry-forward — after [Enhancement] Monitor: timeline-wide network diagram with inactive nodes greyed out and MAC-grouped DHCP clients #510 settles MAC-keyed identity.

Worth noting for triage value: the existing priority model is well-designed and already does the hard part (conflict resolution with explicit authority ordering). Most of this issue is feeding it more inputs, not rebuilding it.

Related: #510 (MAC-grouped identity across snapshots — the natural key for hostname carry-forward), #501 (unknown protocols — hostname presence/absence is a triage signal), #509 (diagram rendering — hostnames are the label that makes a diagram readable), #499 (conflicting classification axes — hostname conflicts are a similar "sources disagree" problem).

Relevant files:

  • backend/src/main/java/com/tracepcap/analysis/service/HostnameResolverService.java:37-99 (sources, priority, tshark filter)
  • backend/src/main/java/com/tracepcap/analysis/service/NdpiService.java:69-70, 129 (Hostname/SNI already extracted)
  • backend/src/main/java/com/tracepcap/analysis/entity/ConversationEntity.java:80-96 (hostname, tlsSubject, tlsIssuer)
  • backend/src/main/java/com/tracepcap/analysis/service/AnalysisService.java:145-163 (where hostnames are threaded into classification)
  • backend/src/main/java/com/tracepcap/hostlog/service/WebServerLogExtractor.java (existing HTTP pass — http.host could join it)
  • frontend/src/components/common/HostnameSourceBadge/HostnameSourceBadge.tsx

Metadata

Metadata

Assignees

No one assigned

    Labels

    analysisPacket analysis featuresdnsDNS-related featuresenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions