//
// 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) ;
}
}
}