MX on the wire, and what it takes to query it from an iPhone
An MX record is one of the simplest resource records in DNS and one of the most consequential. Type 15, class IN, and an RDATA section with exactly two parts: a 16-bit preference in network byte order, followed by a domain name for the exchange. That name is subject to DNS name compression, so it is frequently stored as a pointer back into an earlier part of the message rather than as a literal string — which is why you cannot read an MX answer by treating the packet as text.
RFC 5321 section 5.1 defines what a sending MTA does with the set. It queries MX for the recipient domain, sorts the results by preference ascending, resolves each exchange to address records, and attempts delivery in that order, randomising among records that share a preference value. If the MX query returns nothing, the sender falls back to the address records of the domain itself — the implicit MX rule. If it returns a single record with preference 0 and an exchange of a lone dot, RFC 7505 says the domain accepts no mail and the sender must bounce immediately instead of retrying for five days. The exchange must be a hostname with address records; it must not be a CNAME and must not be an address literal.
Getting that answer on macOS or Windows is straightforward. SSHive uses Node's c-ares-backed resolver, which speaks DNS directly to the servers your OS is configured with rather than going through getaddrinfo. resolveMx returns the set, sorted ascending. Each exchange is then passed to resolve4, each resulting address to a PTR lookup, and the first address of each exchange to the DNSBL engine, which fans all eight zones out in parallel. Every enrichment step is independently error-trapped, so an exchange with no PTR record or no A record degrades to an em dash instead of failing the whole lookup. None of this needs a raw socket, which is why MX Lookup behaves identically in the Mac App Store build — unlike traceroute, which the sandbox genuinely blocks.
iOS is the harder problem. Apple's high-level networking gives you name-to-address resolution and nothing more: getaddrinfo and NWEndpoint return A and AAAA records, and there is no public Swift API for an arbitrary record type. That is precisely why SSHive's iOS DNS Lookup screen shows A and AAAA only. To read MX, SSHive drops to the BSD resolver in libresolv through a small C helper: res_init, then res_query with class C_IN and type T_MX for the raw answer, then ns_initparse and ns_parserr over the answer section, reading the preference big-endian from the first two RDATA bytes and expanding the compressed exchange name with dn_expand. Swift sorts the result by preference and hands it to the view. No third-party resolver library, no web API in the path — the query goes from your device to the resolvers it is already using.
The costs are honest ones. The query uses the system resolver configuration, so a captive portal or a DNS64/NAT64 network that mishandles type 15 makes res_query fail and the app reports no MX records rather than a network error. The parser stops at 32 records. And across every platform there is no custom nameserver field, no TTL display, and no SPF, DKIM, DMARC, MTA-STS or TLS-RPT inspection.