Skip to content
Haiku OSDeep Dive Published Updated 7 min readViews unavailable

Haiku BNetworkAddressResolver: DNS Results and Address Families

Resolve Haiku host and service names with BNetworkAddressResolver, iterate address candidates safely, and keep DNS failure separate from connection failure.

BNetworkAddressResolver maps a host or numeric address and an optional service or port into one or more BNetworkAddress candidates. It is a Network Kit wrapper around address-resolution behavior, not a socket connection and not proof that a remote service is reachable. Keeping resolution separate from connection setup gives an application better error messages and a way to try multiple address-family candidates.

Name resolution can depend on configuration, available address families, service databases, and the network environment. Do not resolve once at application startup and cache the first address forever. DNS answers and interface availability can change while the process remains open. Resolve near the operation, apply an explicit cache policy where appropriate, and handle each candidate’s connection result separately.

Choose the resolver input form

Constructors and SetTo() accept an address plus a numeric port, a service name, or an explicit address family. The static Resolve() methods return a BReference<const BNetworkAddressResolver>, which expresses shared lifetime management. Check that the returned reference is valid and call InitCheck() before enumerating. The resolver’s status is a resolution result, not the later socket’s connect result.

When user input can be either an IPv4/IPv6 literal or a host name, use a form that permits normal address resolution and let the API interpret it. If the application only wants numeric input, use the documented B_NO_ADDRESS_RESOLUTION flag so a typo is reported as invalid instead of unexpectedly triggering a lookup. B_UNCONFIGURED_ADDRESS_FAMILIES changes which address families are considered when they are not configured; use it only when the application has a reason to include those candidates.

Service names can map to a port through system service configuration. If the application protocol requires a specific port, use the numeric port and show that exact value in diagnostics. A successful service-name lookup does not mean the remote program speaks the expected protocol on that port.

Iterate all candidates

GetNextAddress() uses a cookie. Initialize it to zero before the first call and continue until the documented end status. The overload accepting a family lets the caller inspect one family at a time. Trying only the first result can fail on systems where IPv6 is listed before an unreachable candidate while a later IPv4 address works, or vice versa.

Do not assume the result order is a permanent preference policy. Use a bounded connect strategy appropriate to the app, with timeouts and an explicit attempt order. Avoid serially waiting a long time on every unreachable address. If using concurrent “happy eyeballs”-style connection attempts, cancel losing attempts and ensure only one connection is adopted.

The resolver returns BNetworkAddress values, not raw strings. Preserve family and port while passing the address to the socket API. If formatting a result for a UI, use the address object’s family-aware conversion methods; IPv6 literals need brackets when combined with ports in endpoint text.

Separate DNS failure from transport failure

Use distinct status and error messages for invalid input, resolution failure, address-family mismatch, connect timeout, refused connection, TLS failure, and application-protocol rejection. An app that reports “DNS is broken” when a server refuses a TCP connection sends the user to the wrong layer. Capture the hostname/service, resolver status, candidate families, selected address, and later connect status.

A positive DNS answer can point to a stale or intentionally unavailable endpoint. A numeric literal can skip name lookup yet still fail at routing or transport. Conversely, a failed hostname resolution can result from local resolver settings or transient service loss. Do not retry without a limit or turn a resolution failure into an unbounded UI wait.

BNetworkAddressResolver is synchronous at the SetTo/Resolve boundary in the current implementation. DNS may block while the system performs lookup work. Run it off the window event thread for interactive applications and deliver results back through a messenger. Add cancellation and operation identity so an old slow lookup cannot overwrite a newer user request.

When an address is returned, inspect its family and port before constructing the socket. A hostname may yield several records, and a service may yield a port value that differs by protocol. The resolver’s success does not mean every returned candidate is suitable for the application’s socket type. Match the address family to the socket implementation, and do not accidentally drop the service port while copying the result.

Keep host and service input separate in the model. Parsing a string such as host:port is ambiguous for IPv6 literals unless bracket syntax is handled correctly; prefer a UI with explicit host and port fields or use BUrl parsing for URL input. If the application accepts a service name, validate its length and allowed character set, and report whether failure occurred while interpreting the service or looking up the host.

Lifetime and caching

The static Resolve() APIs return reference-counted resolver objects and are the preferred interface mentioned in the header for using the internal cache. The header itself notes that not every Resolve variant is implemented as needed and that SetTo() constructors could be removed in favor of Resolve(); check the target Haiku revision for the exact available overloads. Do not assume cache lifetime, TTL, or invalidation behavior beyond what the public documentation guarantees.

Because the returned object is reference-counted, keep a BReference while enumerating and release it when the operation ends. Do not retain a raw pointer from the reference after it is destroyed. Copy selected addresses into an operation object if the connection attempt outlives the immediate resolver scope.

Avoid application-level infinite caches. If caching is necessary, define a short, explicit freshness policy, preserve all family candidates, and allow retry after interface or network changes. Keep the resolver object separate from the authenticated connection: a DNS update must not silently replace a live TLS peer without the application’s normal identity checks.

For endpoint text, distinguish a presentation string from a resolver key. Normalize the host according to the input format you actually support; do not lowercase or rewrite opaque service data without checking its rules. Avoid logging full URLs that may contain user information. A support log can record that resolution used a hostname, the family mask, the number of candidates, and elapsed time while redacting credentials, paths, and query parameters.

The resolver should also be treated as a snapshot even when an internal cache is involved. Keep the selected candidate alongside the operation that consumes it, and include a generation or request ID in asynchronous result messages. On cancellation, ignore the late result and release its BReference rather than updating a newer connection attempt.

A bounded enumeration example

BReference<const BNetworkAddressResolver> resolver
    = BNetworkAddressResolver::Resolve(AF_UNSPEC, host, port);
if (resolver == NULL || resolver->InitCheck() != B_OK)
    return B_NAME_NOT_FOUND;

uint32 cookie = 0;
BNetworkAddress candidate;
while (resolver->GetNextAddress(&cookie, candidate) == B_OK) {
    // Add a bounded candidate copy to the connection attempt list.
}

The exact Resolve() overload must match the current public header. Production code should distinguish an invalid BReference, InitCheck() failure, and the resolver’s normal end-of-list status. Bound the number of candidates and total connection time. The excerpt does not perform a network connection.

Testing resolution behavior

Test a numeric IPv4 literal, a numeric IPv6 literal, a valid hostname with multiple records, an unknown hostname, a service name, an invalid service, an explicit family constraint, B_NO_ADDRESS_RESOLUTION, and a temporarily unavailable network. Verify that enumeration handles end-of-list separately from failure.

Change DNS or network configuration between operations and confirm the application does not keep using a stale cached address forever. Test a hostname where the first candidate cannot connect but a later one can. Ensure a slow resolution does not block window redraw or quit handling. Confirm a newer request cannot be overwritten by a late result from an older lookup.

For support reports, include Haiku revision, input host/service with sensitive values redacted, flags, resolver result, candidates and families, time spent resolving, and the selected connect result. Never log credentials embedded in URLs or user-specific query data.

BNetworkAddressResolver provides candidates and their address-family structure. A reliable client treats those candidates as inputs to a bounded connection strategy, keeps resolution and connection errors distinct, and refreshes assumptions as the network changes.

Related:

Sources:

Comments