// // File: hzDNS.cpp // // Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com) // // The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free // Software Foundation, either version 3 of the License, or any later version. // // The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR // A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses. //
// // DNS/MX Record lookup functions //
#include <iostream>
#include <netdb.h> #include <arpa/inet.h> #include <resolv.h> #include <string.h>
#include "hzChars.h" #include "hzString.h" #include "hzErrcode.h" #include "hzProcess.h" #include "hzDNS.h"
/* ** Variables */
static bool s_bInitResolver ; // Resolver initialized flag
hzEcode GetHostByAddr (hzString& Host, const char* cpIPAddr) { // Category: Internet // // GetHostByAddr: Populate the supplied string with the servername (hostname) at the supplied IP address. Return the DNS // return code. // // Arguments: 1) Host The Hostname (at the supplied IP address) // 2) cpIPAddr The IP address // // Returns: E_DNS_NOHOST If the host is unknown. // E_DNS_NODATA If the name is valid but does not have an IP address. // E_DNS_FAILED If a non-recoverable name server error occurred. // E_DNS_RETRY If a temporary error occurred on an authoritative name server. // E_OK If the hostname is found by the DNS
_hzfunc(__func__) ;
HOSTENT* pHost ; // Pointer to host from gethostbyaddr() in_addr x ; // Internet address translated by inet_addr()
x.s_addr = inet_addr(cpIPAddr) ;
// Unix only: win xlate: pHost = gethostbyaddr(cpIPAddr, 4, AF_INET) ; pHost = gethostbyaddr(&x, 4, AF_INET) ;
Host.Clear() ; if (pHost) { Host = pHost->h_name ; return E_OK ; }
switch (h_errno) { case HOST_NOT_FOUND: return E_DNS_NOHOST ; // The host is unknown. case NO_DATA: return E_DNS_NODATA ; // The name is valid but does not have an IP address. case NO_RECOVERY: return E_DNS_FAILED ; // A non-recoverable name server error occurred. case TRY_AGAIN: return E_DNS_RETRY ; // A temporary error occurred on an authoritative name server. }
return E_DNS_FAILED ; }
hzString hzDNS::_procraw (uchar** cpPtr) { // Obtain a null terminated server name from the DNS response buffer // // The supplied argument is a pointer to the pointer into the DNS response buffer. The string extraction process is as follows:- // // We assume we are one a byte which either describes a lenght or a location. A byte of 0xC0 or greater states that the next byte // specifies a location (within the first 240 bytes of the buffer) where the string can be found. A byte lower than this indicates // the length of the string which can be found directly after this byte. // // Note that a string can be comprised of a mix of earlier strings (0xC0) and new strings. // // Arguments: 1) cpPtr A pointer to the pointer into the DNS response buffer // // Returns: Instance of hzString by value being server name.
_hzfunc("hzDNS::_procraw") ;
hzChain Z ; // For formulating string to be returned uchar* p ; // Pointer into orig buf uchar* i ; // DNS response iterator hzString S ; // String to be returned (domain/server name) uint32_t len ; // Length of pre-parsed chunk uint32_t c ; // Counter upto length uint32_t nOset ; // Offset into orig buf
/* ** Process answer */
i = *cpPtr ;
for (; *i ;) { if (*i & 0xC0) { // Can we use an offset to a pre parsed base name? nOset = ((*i & 0x3F) << 8) + i[1] ;
if (nOset < 240) { // Yes - Set p to this locn and proceed until a 0 i += 2 ; p = m_cpDns + nOset ;
for (; *p ;) { if (*p & 0xC0) { nOset = ((*p & 0x3F) << 8) + p[1] ; p = m_cpDns + nOset ; }
// Get no of bytes before next period len = (uint32_t) *p ; p++ ;
for (c = 0 ; c < len ; c++) Z.AddByte(*p++) ; if (*p) Z.AddByte(CHAR_PERIOD) ; } break ; } }
// No backward pre-parsed to be applied to string, just len and string len = (int) *i ; p = i + 1 ; i = p + len ;
for (c = 0 ; c < len ; c++) Z.AddByte(*p++) ; if (*p) Z.AddByte(CHAR_PERIOD) ;
if (i[0] == 0) { i++ ; break ; } }
*cpPtr = i ; S = Z ; return S ; }
void hzDNS::_clear (void) { // Resets the hzDNS instance back to an unpopulated state. Note this does not delete the operating buffer. This is only deleted // by the destructor. // // Arguments: None // Returns: None
m_arQus.Clear() ; m_arAns.Clear() ; m_arAut.Clear() ; m_arAdd.Clear() ;
m_qID = 0 ; m_DNA = 0 ; //m_nQus = 0 ; //m_nAns = 0 ; //m_nAut = 0 ; //m_nAdd = 0 ;
if (m_cpDns) memset(m_cpDns, 0, 2048) ; }
hzEcode hzDNS::Query (const char* dom, DnsType eType) { // Do the actual DNS query. // // Arguments: 1) eType Query type Either MX or A // 2) dom Domain name // // Returns: E_ARGUMENT If the domain name is not supplied // E_INITFAIL If res_init() has not been called before and now returns an error // E_OVERFLOW If the res_search function owverfills the buffer // E_DNS_NOHOST If the host does not exist // E_DNS_NODATA If the domain exists but there is no host for the applicable service // E_DNS_RETRY If the DNS could not provide data at this time // E_DNS_FAILED If the DNS failed // E_OK If the DNS query was successful
_hzfunc("hzDNS::Query") ;
hzChain errCh ; // In the event of error, shows progress so far DnsRec dr ; // DNS record hzLogger* pLog ; // For debug mode uchar* ix ; // DNS record iterator uchar* jx ; // DNS record forward marker uint32_t nQus ; // Questions uint32_t nAns ; // Questions uint32_t nAut ; // Questions uint32_t nAdd ; // Questions uint32_t nCount ; // Interator res_search response int32_t nSize ; // Bytes returned by res_search
// Clear m_Error m_Error.Clear() ;
// Check args if (!dom || !dom[0]) return hzerr(E_ARGUMENT, "No domain name supplied") ;
// Check buffer if (!m_cpDns) m_cpDns = new uchar[2048] ; ix = m_cpDns ; if (!ix) hzexit(E_MEMORY, "Could not allocate buffers for DNS query") ;
// Chekck res_init() has been called (only once) if (!s_bInitResolver) { if (res_init() == -1) return hzerr(E_INITFAIL, "Could not init DNS resolver") ; s_bInitResolver = true ; } _clear() ;
pLog = GetThreadLogger() ;
/* ** Query the DNS */
nSize = res_search(dom, C_IN, eType, m_cpDns, 2047) ;
if (nSize >= 2000) return hzerr(E_OVERFLOW, "DNS buffer overflow for %s", dom) ;
if (nSize == -1) { switch (h_errno) { case HOST_NOT_FOUND: return E_DNS_NOHOST ; // The specified host is unknown. case NO_DATA: return E_DNS_NODATA ; // The requested name is valid but does not have an IP address. case NO_RECOVERY: return E_DNS_FAILED ; // A non-recoverable name server error occurred. case TRY_AGAIN: return E_DNS_RETRY ; // A temporary error occurred on an authoritative name server. Try again later. }
return hzerr(E_DNS_FAILED, "Unspecified error (%s) for domain %s", hstrerror(h_errno), dom) ; }
m_cpDns[nSize] = 0 ;
if (_hzGlobal_Debug & HZ_DEBUG_DNS) { errCh.Printf("DNS BUFFER (%d bytes) =\n[\n", nSize) ; for (nCount = 0 ; nCount < (uint32_t) nSize ;) { if (nCount < 240 && m_cpDns[nCount] > 32 && m_cpDns[nCount] < 128) errCh.Printf(" %c", m_cpDns[nCount]) ; else errCh.Printf(" %02x", m_cpDns[nCount]) ;
nCount++ ; if (!(nCount % 40)) errCh.AddByte(CHAR_NL) ; else errCh.AddByte(CHAR_SPACE) ; } if (nCount % 40) errCh.AddByte(CHAR_NL) ; errCh.Printf("]\n") ; }
// Get result params m_qID = (m_cpDns[0] << 8) + m_cpDns[1] ; m_DNA = (m_cpDns[2] << 8) + m_cpDns[3] ; nQus = (m_cpDns[4] << 8) + m_cpDns[5] ; nAns = (m_cpDns[6] << 8) + m_cpDns[7] ; nAut = (m_cpDns[8] << 8) + m_cpDns[9] ; nAdd = (m_cpDns[10] << 8) + m_cpDns[11] ;
if (_hzGlobal_Debug & HZ_DEBUG_DNS) { errCh.Printf("Query id: %d\n", m_qID) ; errCh.Printf("DNA code: %d\n", m_DNA) ; errCh.Printf("No questions: %d\n", nQus) ; errCh.Printf("No answers: %d\n", nAns) ; errCh.Printf("No authority: %d\n", nAut) ; errCh.Printf("No additional: %d\n", nAdd) ; }
/* ** Bypass the 'pre-defined' strings to get the answers */
for (ix = m_cpDns + 12 ; *ix ; ix++) ; ix += 5 ;
// Now have answers for (nCount = 0 ; nCount < nAns ; nCount++) { dr.Clear() ;
dr.m_Domain = _procraw(&ix) ; if (!dr.m_Domain) { pLog->Out(errCh) ; return hzerr(E_DNS_FAILED, "Answer record without domain") ; }
if (_hzGlobal_Debug & HZ_DEBUG_DNS) errCh.Printf("Answ: %s:", *dr.m_Domain) ;
dr.m_nType = (ix[0] << 8) + ix[1] ; dr.m_nClass = (ix[2] << 8) + ix[3] ; dr.m_nTTL = (ix[4] << 24) + (ix[5] << 16) + (ix[6] << 8) + ix[7] ; dr.m_nLen = (ix[8] << 8) + ix[9] ; dr.m_nValue = 0 ; ix += 10 ; jx = ix + dr.m_nLen ;
if (_hzGlobal_Debug & HZ_DEBUG_DNS) errCh.Printf(" -> Type %u Class %u TTL %u Len %u", dr.m_nType, dr.m_nClass, dr.m_nTTL, dr.m_nLen) ;
if (dr.m_nType == DNSTYPE_MX) { dr.m_nValue = (ix[0] << 8) + ix[1] ; ix += 2 ;
if (_hzGlobal_Debug & HZ_DEBUG_DNS) errCh.Printf(" Val %u", dr.m_nValue) ; } if (dr.m_nType == DNSTYPE_A) { dr.m_Ipa.SetValue(ix[0], ix[1], ix[2], ix[3]) ;
if (_hzGlobal_Debug & HZ_DEBUG_DNS) errCh.Printf(" IPv4 (%u.%u.%u.%u)\n", ix[0], ix[1], ix[2], ix[3]) ; } else if (dr.m_nType == DNSTYPE_AAAA) { dr.m_anorakA = (ix[0] << 24) + (ix[1] << 16) + (ix[2] << 8) + (ix[3]) ; dr.m_anorakB = (ix[4] << 24) + (ix[5] << 16) + (ix[6] << 8) + (ix[7]) ; dr.m_anorakC = (ix[8] << 24) + (ix[9] << 16) + (ix[10] << 8) + (ix[11]) ; dr.m_anorakD = (ix[12] << 24) + (ix[13] << 16) + (ix[14] << 8) + (ix[15]) ;
if (_hzGlobal_Debug & HZ_DEBUG_DNS) errCh.Printf(" IPv6 (%u.%u.%u.%u)\n", dr.m_anorakA, dr.m_anorakB, dr.m_anorakC, dr.m_anorakD) ; } else { if (_hzGlobal_Debug & HZ_DEBUG_DNS) errCh.AddByte(CHAR_NL) ;
dr.m_Server = _procraw(&ix) ; if (!dr.m_Server) { pLog->Out(errCh) ; return hzerr(E_DNS_FAILED, "Answer record without server") ; } }
m_arAns.Add(dr) ; ix = jx ; }
// Get authority records for (nCount = 0 ; nCount < nAut ; nCount++) { dr.Clear() ;
// Get the domain name
dr.m_Domain = _procraw(&ix) ; if (!dr.m_Domain) { pLog->Out(errCh) ; return hzerr(E_DNS_FAILED, "Authority record without domain") ; }
if (_hzGlobal_Debug & HZ_DEBUG_DNS) errCh.Printf("Auth: %s:", *dr.m_Domain) ;
dr.m_nType = (ix[0] << 8) + ix[1] ; dr.m_nClass = (ix[2] << 8) + ix[3] ; dr.m_nTTL = (ix[4] << 24) + (ix[5] << 16) + (ix[6] << 8) + ix[7] ; dr.m_nLen = (ix[8] << 8) + ix[9] ; dr.m_nValue = 0 ; ix += 10 ; jx = ix + dr.m_nLen ;
if (_hzGlobal_Debug & HZ_DEBUG_DNS) errCh.Printf(" -> Type %u Class %u TTL %u Len %u\n", dr.m_nType, dr.m_nClass, dr.m_nTTL, dr.m_nLen) ;
if (dr.m_nType == DNSTYPE_MX) { dr.m_nValue = (ix[0] << 8) + ix[1] ; ix += 2 ; }
if (dr.m_nType == DNSTYPE_A) dr.m_Ipa.SetValue(ix[0], ix[1], ix[2], ix[3]) ; else if (dr.m_nType == DNSTYPE_AAAA) { dr.m_anorakA = (ix[0] << 24) + (ix[1] << 16) + (ix[2] << 8) + (ix[3]) ; dr.m_anorakA = (ix[4] << 24) + (ix[5] << 16) + (ix[6] << 8) + (ix[7]) ; dr.m_anorakA = (ix[8] << 24) + (ix[9] << 16) + (ix[10] << 8) + (ix[11]) ; dr.m_anorakA = (ix[12] << 24) + (ix[13] << 16) + (ix[14] << 8) + (ix[15]) ; ix += 16 ; } else { dr.m_Server = _procraw(&ix) ; if (!dr.m_Server) { pLog->Out(errCh) ; return hzerr(E_DNS_FAILED, "Authority record without server") ; } }
m_arAut.Add(dr) ; ix = jx ; }
#if 0 // Get additional records for (nCount = 0 ; nCount < nAdd ; nCount++) { dr.Clear() ;
dr.m_Domain = _procraw(&ix) ; if (!dr.m_Domain) { pLog->Out(errCh) ; return hzerr(E_DNS_FAILED, "Additional record without domain") ; }
if (_hzGlobal_Debug & HZ_DEBUG_DNS) errCh.Printf("Addd: %s:", *dr.m_Domain) ;
dr.m_nType = (ix[0] << 8) + ix[1] ; dr.m_nClass = (ix[2] << 8) + ix[3] ; dr.m_nTTL = (ix[4] << 24) + (ix[5] << 16) + (ix[6] << 8) + ix[7] ; dr.m_nLen = (ix[8] << 8) + ix[9] ; dr.m_nValue = 0 ; ix += 10 ; jx = ix + dr.m_nLen ;
if (_hzGlobal_Debug & HZ_DEBUG_DNS) errCh.Printf(" -> Type %u Class %u TTL %u Len %u\n", dr.m_nType, dr.m_nClass, dr.m_nTTL, dr.m_nLen) ;
if (dr.m_nType == DNSTYPE_MX) { dr.m_nValue = (ix[0] << 8) + ix[1] ; ix += 2 ; }
if (dr.m_nType == DNSTYPE_A) dr.m_Ipa.SetValue(ix[0], ix[1], ix[2], ix[3]) ; else if (dr.m_nType == DNSTYPE_AAAA) { dr.m_anorakA = (ix[0] << 24) + (ix[1] << 16) + (ix[2] << 8) + (ix[3]) ; dr.m_anorakA = (ix[4] << 24) + (ix[5] << 16) + (ix[6] << 8) + (ix[7]) ; dr.m_anorakA = (ix[8] << 24) + (ix[9] << 16) + (ix[10] << 8) + (ix[11]) ; dr.m_anorakA = (ix[12] << 24) + (ix[13] << 16) + (ix[14] << 8) + (ix[15]) ; ix += 16 ; } else { dr.m_Server = _procraw(&ix) ; if (!dr.m_Server) { pLog->Out(errCh) ; return hzerr(E_DNS_FAILED, "Additional record without server") ; } }
m_arAdd.Add(dr) ; ix = jx ; } #endif
return E_OK ; }
hzEcode hzDNS::SelectMX (hzList<hzResServer>& ServersMX, const char* dom) { // Select mail servers for a domain // // Arguments: 1) ServersMX The list of mail servers // 2) dom Domain name // // Returns: E_ARGUMENT If the domain name is not supplied // E_INITFAIL If res_init() has not been called before and now returns an error // E_OVERFLOW If the res_search function owverfills the buffer // E_DNS_NOHOST If the host does not exist // E_DNS_NODATA If the domain exists but there is no host for the applicable service // E_DNS_RETRY If the DNS could not provide data at this time // E_DNS_FAILED If the DNS failed // E_OK If the DNS query was successful
_hzfunc("hzDNS::SelectMX") ;
hzList<hzResServer> smx ; // Server names from MX query hzList<hzResServer>::Iter I ; // Server interator hzList<DnsRec>::Iter ri ; // DNS record iterator (primary) hzList<DnsRec>::Iter si ; // DNS record iterator (secondary)
hzDNS dq ; // DNS query primary hzDNS dq2 ; // DNS query secondary hzResServer S ; // Server instance hzResServer X ; // Server instance DnsRec dr ; // DNS record DnsRec dr2 ; // DNS record hzEcode rc = E_OK ; // Return code from DNS lookup functions
// Clear m_Error m_Error.Clear() ;
// Check args if (!dom || !dom[0]) return hzerr(E_ARGUMENT, "No domain name supplied") ;
// Lookup MX records in DNS rc = dq.QueryMX(dom) ; if (rc != E_OK) return rc ;
if (_hzGlobal_Debug & HZ_DEBUG_DNS) m_Error.Printf("DNS found %d answers, %d auth and %d additional\n", dq.NoAnswers(), dq.NoAuth(), dq.NoAdditional()) ;
// Now have MX records for the domain. Sometimes mail servers listed here will have multiple IP addresses. for (ri = dq.m_arAns ; ri.Valid() ; ri++) { dr = ri.Element() ;
if (dr.m_Ipa == IPADDR_NULL || dr.m_Ipa == IPADDR_LOCAL) { // Providing the server has a server name, its IP address can be found by a type A query if (_hzGlobal_Debug & HZ_DEBUG_DNS) m_Error.Printf("Domain %s Server %s - NULL IP (case 1)\n", *dr.m_Domain, *dr.m_Server) ; //continue ; }
S.m_Servername = dr.m_Server ; S.m_nValue = dr.m_nValue ; S.m_Ipa = dr.m_Ipa ; ServersMX.Add(S) ; }
/* if (!ServersMX.Count()) { // This is where the initial answer did not for whatever reason, provide an IP address along with the servername.
if (_hzGlobal_Debug & HZ_DEBUG_DNS) m_Error.Printf("Initial pass did not produce IP addresses for the mail servers\n") ;
for (ri = dq.m_arAns ; ri.Valid() ; ri++) { dr = ri.Element() ;
// Do a direct (type A) query on the mail server's name rc = dq2.QueryA(*dr.m_Domain) ; if (rc != E_OK) { if (_hzGlobal_Debug & HZ_DEBUG_DNS) m_Error.Printf("DNS Unavailable (case 2)\n") ; return rc ; }
if (!dq2.NoAnswers()) { if (_hzGlobal_Debug & HZ_DEBUG_DNS) m_Error.Printf("Type A query on mail-server <%s> produced no answers\n", *dr.m_Server) ; return E_DNS_FAILED ; }
for (si = dq2.m_arAns ; si.Valid() ; si++) { dr2 = si.Element() ;
if (dr2.m_Ipa == IPADDR_NULL || dr2.m_Ipa == IPADDR_LOCAL) { if (_hzGlobal_Debug & HZ_DEBUG_DNS) m_Error.Printf("Domain %s Server %s - NULL IP (case 2)\n", *dr2.m_Domain, *dr2.m_Server) ; continue ; }
S.m_Servername = dr2.m_Server ; S.m_nValue = dr2.m_nValue ; S.m_Ipa = dr2.m_Ipa ; ServersMX.Add(S) ; } } }
if (!ServersMX.Count()) { // This is where the initial answer did not for whatever reason, provide an IP address along with the servername.
if (_hzGlobal_Debug & HZ_DEBUG_DNS) m_Error.Printf("Initial pass did not produce IP addresses for the mail servers\n") ;
for (ri = dq.m_arAns ; ri.Valid() ; ri++) { dr = ri.Element() ;
// Do a direct (type A) query on the mail server's name rc = dq2.QueryA(*dr.m_Server) ; if (rc != E_OK) { if (_hzGlobal_Debug & HZ_DEBUG_DNS) m_Error.Printf("DNS Unavailable (case 3)\n") ; return rc ; }
if (!dq2.NoAnswers()) { if (_hzGlobal_Debug & HZ_DEBUG_DNS) m_Error.Printf("Type A query on mail-server <%s> produced no answers\n", *dr.m_Server) ; return E_DNS_FAILED ; }
for (si = dq2.m_arAns ; si.Valid() ; si++) { dr2 = si.Element() ;
if (dr2.m_Ipa == IPADDR_NULL || dr2.m_Ipa == IPADDR_LOCAL) { if (_hzGlobal_Debug & HZ_DEBUG_DNS) m_Error.Printf("Domain %s Server %s - NULL IP (case 3)\n", *dr2.m_Domain, *dr2.m_Server) ; continue ; }
S.m_Servername = dr2.m_Server ; S.m_nValue = dr2.m_nValue ; S.m_Ipa = dr2.m_Ipa ; ServersMX.Add(S) ; } } } */
return rc ; }
void hzDNS::Show (hzChain& Result) const { // Output search results to the supplied hzChain // // Arguments: 1) Result The output chain // // Returns: None
_hzfunc("hzDNS::Show") ;
hzList<DnsRec>::Iter ir ; // DNS record iterator
DnsRec dr ; // DNS record
// Print the results
Result.Printf("Querry id: %d\n", m_qID) ; Result.Printf("Questions: %d\n", m_arQus.Count()) ; Result.Printf("Answers: %d\n", m_arAns.Count()) ; Result.Printf("Authorative: %d\n", m_arAut.Count()) ; Result.Printf("Additional: %d\n", m_arAdd.Count()) ;
if (m_arAns.Count()) { Result.Printf("\n;; ANSWER SECTION Type Class TTL Len Value Server\n") ;
for (ir = m_arAns ; ir.Valid() ; ir++) { dr = ir.Element() ;
if (dr.m_Server) Result.Printf("%-29s %4d %4d %6d %4d %4d %-30s", *dr.m_Domain, dr.m_nType, dr.m_nClass, dr.m_nTTL, dr.m_nLen, dr.m_nValue, *dr.m_Server) ; else Result.Printf("%-29s %4d %4d %6d %4d %4d", *dr.m_Domain, dr.m_nType, dr.m_nClass, dr.m_nTTL, dr.m_nLen, dr.m_nValue) ;
if (dr.m_Ipa) Result.Printf("\t(%s)", *dr.m_Ipa) ; Result.AddByte(CHAR_NL) ; } }
if (m_arAut.Count()) { Result.Printf("\n;; AUTHORITY SECTION Type Class TTL Len Value Server Address\n") ;
for (ir = m_arAut ; ir.Valid() ; ir++) { dr = ir.Element() ;
if (dr.m_Server) Result.Printf("%-29s %4d %4d %6d %4d %4d %-30s", *dr.m_Domain, dr.m_nType, dr.m_nClass, dr.m_nTTL, dr.m_nLen, dr.m_nValue, *dr.m_Server) ; else Result.Printf("%-29s %4d %4d %6d %4d %4d", *dr.m_Domain, dr.m_nType, dr.m_nClass, dr.m_nTTL, dr.m_nLen, dr.m_nValue) ;
if (dr.m_Ipa) Result.Printf("\t(%s)", *dr.m_Ipa) ; Result.AddByte(CHAR_NL) ; } }
if (m_arAdd.Count()) { Result.Printf("\n;; ADDITIONAL SECTION Type Class TTL Len Value Server Address\n") ;
for (ir = m_arAdd ; ir.Valid() ; ir++) { dr = ir.Element() ;
if (dr.m_Server) Result.Printf("%-29s %4d %4d %6d %4d %4d %-30s", *dr.m_Domain, dr.m_nType, dr.m_nClass, dr.m_nTTL, dr.m_nLen, dr.m_nValue, *dr.m_Server) ; else Result.Printf("%-29s %4d %4d %6d %4d %4d", *dr.m_Domain, dr.m_nType, dr.m_nClass, dr.m_nTTL, dr.m_nLen, dr.m_nValue) ;
if (dr.m_Ipa) Result.Printf("\t(%s)", *dr.m_Ipa) ; Result.AddByte(CHAR_NL) ; } } }