yate/engine/Resolver.cpp

852 lines
23 KiB
C++

/**
* Resolver.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*
* This program 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.
*/
/*
Preprocessor definitions:
WINDOWS:
HAVE_DNS_NAPTR_DATA: Define it if DNS_NAPTR_DATA is defined in windns.h
If defined, NAPTR query will work at runtime on all supported versions
If not defined NAPTR query will work only if Windows major version is less then 6
Non Windows
NO_RESOLV: Defined if the resolver is not available at compile time
NO_DN_SKIPNAME: Define it if __dn_skipname() is not available at link time
*/
#include "yateclass.h"
#ifdef _WINDOWS
#include <windns.h>
#elif !defined(NO_RESOLV)
#include <resolv.h>
#include <arpa/nameser.h>
#endif // _WINDOWS
using namespace TelEngine;
// Resolver type names
const TokenDict Resolver::s_types[] = {
{ "SRV", Srv },
{ "NAPTR", Naptr },
{ "A", A4 },
{ "AAAA", A6 },
{ "TXT", Txt },
{ 0, 0 },
};
#ifdef _WINDOWS
class WindowsVersion
{
public:
WindowsVersion();
inline unsigned int major() const
{ return m_major; }
private:
unsigned int m_major;
};
WindowsVersion::WindowsVersion()
: m_major(0)
{
OSVERSIONINFOA ver;
::ZeroMemory(&ver,sizeof(OSVERSIONINFOA));
ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (::GetVersionEx(&ver))
m_major = ver.dwMajorVersion;
if (!m_major)
Debug(DebugWarn,"Resolver failed to detect Windows version");
}
static WindowsVersion s_winVer;
#endif
// Utility: print the result of dns query
// Return the code
static int printResult(int type, int code, const char* dname, ObjList& result, String* error)
{
#ifdef DEBUG
if (!code) {
String s;
int crt = 0;
for (ObjList* o = result.skipNull(); o; o = o->skipNext()) {
DnsRecord* rec = static_cast<DnsRecord*>(o->get());
if (!s)
s << "\r\n-----";
s << "\r\n" << ++crt << ":";
rec->dump(s);
}
if (s)
s << "\r\n-----";
Debug(DebugAll,"%s query for '%s' got %d records%s",
lookup(type,Resolver::s_types),dname,result.count(),s.safe());
}
else {
String dummy;
if (!error) {
error = &dummy;
#ifdef _WINDOWS
Thread::errorString(dummy,code);
#elif defined(__RES)
dummy = hstrerror(code);
#endif
}
Debug(DebugNote,"%s query for '%s' failed with code %d error=%s",
lookup(type,Resolver::s_types),dname,code,TelEngine::c_safe(error));
}
#endif
return code;
}
// Utility: print a record and insert it in list
static bool insertRecord(ObjList& result, DnsRecord* rec, bool ascPref, const char* str)
{
#ifdef XDEBUG
if (rec) {
String s;
rec->dump(s);
Debug(DebugAll,"%s inserting %s",str,s.c_str());
}
#endif
return DnsRecord::insert(result,rec,ascPref);
}
/*
* __dn_skipname() not available at link time
*/
#ifdef NO_DN_SKIPNAME
#define RESOLVER_NAME_COMPRESSED 0xc0 // Name compressed mask
#define RESOLVER_NAME_COMPRESSED_EXT 0x40 // RFC 2671 extended label type
#define RESOLVER_NAME_COMPRESSED_EXT_NEXT 0x41 // RFC 2671 extended label type with length in the next byte
#ifndef dn_skipname
#define dn_skipname __dn_skipname
#endif
// Retrieve the length of an extended compressed or uncompressed name
static int dn_namelen_ext(const unsigned char* buf)
{
if (!buf)
return -1;
unsigned char val = *buf;
unsigned char comp = (val & RESOLVER_NAME_COMPRESSED);
// Compressed but not using extension: error
if (comp == RESOLVER_NAME_COMPRESSED)
return -1;
// Not using extended compression
if (comp != RESOLVER_NAME_COMPRESSED_EXT)
return val;
// Extended compression with length in the next byte
if (val == RESOLVER_NAME_COMPRESSED_EXT_NEXT) {
int nBits = buf[1];
if (!nBits)
nBits = 256;
return (nBits + 7) / 8 + 1;
}
DDebug(DebugNote,"dn_namelen_ext(%p) unknown extended compression %xd",buf,val);
return -1;
}
extern "C" int __dn_skipname(const unsigned char* start, const unsigned char* end)
{
XDebug(DebugNote,"__dn_skipname(%p,%p)",start,end);
const unsigned char* buf = start;
bool ok = true;
while (buf < end) {
unsigned char c = *buf++;
if (!c)
break;
unsigned char comp = (c & RESOLVER_NAME_COMPRESSED);
// Not compressed
if (!comp) {
buf += c;
continue;
}
// Compressed
if (comp == RESOLVER_NAME_COMPRESSED) {
buf++;
break;
}
// Extended compression
if (comp == RESOLVER_NAME_COMPRESSED_EXT) {
int len = dn_namelen_ext(buf - 1);
if (len >= 0) {
buf += len;
continue;
}
ok = false;
break;
}
DDebug(DebugNote,"__dn_skipname: unknown compression type %xd",comp);
// Unknown compression
ok = false;
break;
}
if (!ok || buf > end) {
h_errno = EMSGSIZE;
return -1;
}
return buf - start;
}
#endif // NO_DN_SKIPNAME
// weird but NS_MAXSTRING and dn_string() are NOT part of the resolver API...
#ifndef NS_MAXSTRING
#define NS_MAXSTRING 255
#endif
// copy one string (not domain) from response
static int dn_string(const unsigned char* end, const unsigned char* src, char* dest, int maxlen)
{
if (!src)
return 0;
int n = src[0];
if (!dest)
return n + 1;
maxlen--;
if (maxlen > n)
maxlen = n;
while ((maxlen-- > 0) && (src < end))
*dest++ = *++src;
*dest = 0;
return n + 1;
}
// Dump a record for debug purposes
void DnsRecord::dump(String& buf, const char* sep)
{
buf.append("ttl=",sep) << m_ttl;
if (m_order >= 0)
buf << sep << "order=" << m_order;
if (m_pref >= 0)
buf << sep << "pref=" << m_pref;
}
// Insert a record into a list in the proper location
// Order records ascending by their order
// Look at ascPref for records with the same order
bool DnsRecord::insert(ObjList& list, DnsRecord* rec, bool ascPref)
{
if (!rec || list.find(rec))
return false;
XDebug(DebugAll,"DnsRecord::insert(%p) ttl=%d order=%d pref=%d",
rec,rec->ttl(),rec->order(),rec->pref());
ObjList* a = &list;
ObjList* o = list.skipNull();
for (; o; o = o->skipNext()) {
a = o;
DnsRecord* crt = static_cast<DnsRecord*>(o->get());
if (rec->order() > crt->order())
continue;
if (rec->order() == crt->order()) {
for (; o; o = o->skipNext()) {
DnsRecord* crt = static_cast<DnsRecord*>(o->get());
if (crt->order() != rec->order())
break;
if (crt->pref() == rec->pref())
continue;
if (ascPref == (rec->pref() < crt->pref()))
break;
}
}
break;
}
if (o)
o->insert(rec);
else
a->append(rec);
return true;
}
// Dump a record for debug purposes
void TxtRecord::dump(String& buf, const char* sep)
{
DnsRecord::dump(buf,sep);
buf.append("text=",sep) << "'" << m_text << "'";
}
// Copy a TxtRecord list into another one
void TxtRecord::copy(ObjList& dest, const ObjList& src)
{
dest.clear();
for (ObjList* o = src.skipNull(); o; o = o->skipNext()) {
TxtRecord* rec = static_cast<TxtRecord*>(o->get());
dest.append(new TxtRecord(rec->ttl(),rec->text()));
}
}
// Dump a record for debug purposes
void SrvRecord::dump(String& buf, const char* sep)
{
DnsRecord::dump(buf,sep);
buf.append("address=",sep) << "'" << m_address << "'";
buf << sep << "port=" << m_port;
}
// Copy a SrvRecord list into another one
void SrvRecord::copy(ObjList& dest, const ObjList& src)
{
dest.clear();
for (ObjList* o = src.skipNull(); o; o = o->skipNext()) {
SrvRecord* rec = static_cast<SrvRecord*>(o->get());
dest.append(new SrvRecord(rec->ttl(),rec->order(),rec->pref(),rec->address(),rec->port()));
}
}
NaptrRecord::NaptrRecord(int ttl, int ord, int pref, const char* flags, const char* serv,
const char* regexp, const char* next)
: DnsRecord(ttl,ord,pref),
m_flags(flags), m_service(serv), m_next(next)
{
// use case-sensitive extended regular expressions
m_regmatch.setFlags(true,false);
if (!null(regexp)) {
// look for <sep>regexp<sep>template<sep>
char sep[2] = { regexp[0], 0 };
String tmp(regexp+1);
if (tmp.endsWith(sep)) {
int pos = tmp.find(sep);
if (pos > 0) {
m_regmatch = tmp.substr(0,pos);
m_template = tmp.substr(pos+1,tmp.length()-pos-2);
XDebug(DebugAll,"NaptrRecord match '%s' template '%s'",
m_regmatch.c_str(),m_template.c_str());
}
}
}
}
// Perform the Regexp replacement, return true if succeeded
bool NaptrRecord::replace(String& str) const
{
if (m_regmatch && str.matches(m_regmatch)) {
str = str.replaceMatches(m_template);
return true;
}
return false;
}
// Dump a record for debug purposes
void NaptrRecord::dump(String& buf, const char* sep)
{
DnsRecord::dump(buf,sep);
buf.append("flags=",sep) << "'" << m_flags << "'";
buf << sep << "service=" << "'" << m_service << "'";
buf << sep << "regmatch=" << "'" << m_regmatch << "'";
buf << sep << "template=" << "'" << m_template << "'";
buf << sep << "next=" << "'" << m_next << "'";
}
// Runtime check for resolver availability
bool Resolver::available(Type t)
{
if (t == A6)
return SocketAddr::IPv6 < SocketAddr::AfUnsupported;
#ifdef _WINDOWS
if (t == Naptr) {
if (!s_winVer.major())
return false;
#ifdef HAVE_DNS_NAPTR_DATA
return true;
#else
return s_winVer.major() < 6;
#endif
}
return true;
#elif defined(__RES)
return true;
#endif
return false;
}
// Initialize the resolver in the current thread
bool Resolver::init(int timeout, int retries)
{
if (!available())
return false;
#ifdef _WINDOWS
return true;
#elif defined(__RES)
if ((_res.options & RES_INIT) == 0) {
// need to initialize in current thread
if (res_init())
return false;
}
// always set variables
if (timeout >= 0)
_res.retrans = timeout;
if (retries >= 0)
_res.retry = retries;
return true;
#endif
return false;
}
// Make a query
int Resolver::query(Type type, const char* dname, ObjList& result, String* error)
{
switch (type) {
case Srv:
return srvQuery(dname,result,error);
case Naptr:
return naptrQuery(dname,result,error);
case A4:
return a4Query(dname,result,error);
case A6:
return a6Query(dname,result,error);
case Txt:
return txtQuery(dname,result,error);
default:
Debug(DebugStub,"Resolver query not implemented for type %d",type);
}
return 0;
}
// Make a SRV query
int Resolver::srvQuery(const char* dname, ObjList& result, String* error)
{
int code = 0;
XDebug(DebugAll,"Starting %s query for '%s'",lookup(Srv,s_types),dname);
#ifdef _WINDOWS
DNS_RECORD* srv = 0;
code = (int)::DnsQuery_UTF8(dname,DNS_TYPE_SRV,DNS_QUERY_STANDARD,NULL,&srv,NULL);
if (code == ERROR_SUCCESS) {
for (DNS_RECORD* dr = srv; dr; dr = dr->pNext) {
if (dr->wType != DNS_TYPE_SRV || dr->wDataLength != sizeof(DNS_SRV_DATA))
continue;
DNS_SRV_DATA& d = dr->Data.SRV;
insertRecord(result,new SrvRecord(dr->dwTtl,d.wPriority,d.wWeight,
d.pNameTarget,d.wPort),false,"srvQuery");
}
}
else if (error)
Thread::errorString(*error,code);
if (srv)
::DnsRecordListFree(srv,DnsFreeRecordList);
#elif defined(__RES)
unsigned char buf[512];
int r = res_query(dname,ns_c_in,ns_t_srv,buf,sizeof(buf));
if (r <= 0 || r > (int)sizeof(buf)) {
if (r) {
code = h_errno;
if (error)
*error = hstrerror(code);
}
return printResult(Srv,code,dname,result,error);
}
int queryCount = 0;
int answerCount = 0;
unsigned char* p = buf + NS_QFIXEDSZ;
unsigned char* e = buf + r;
NS_GET16(queryCount,p);
NS_GET16(answerCount,p);
p = buf + NS_HFIXEDSZ;
// Skip queries
for (; queryCount > 0; queryCount--) {
int n = dn_skipname(p,e);
if (n < 0)
break;
p += (n + NS_QFIXEDSZ);
}
for (int i = 0; i < answerCount; i++) {
char name[NS_MAXLABEL + 1];
// Skip this answer's query
int n = dn_expand(buf,e,p,name,sizeof(name));
if ((n <= 0) || (n > NS_MAXLABEL))
break;
buf[n] = 0;
p += n;
// Get record type, class, ttl, length
int rrType, rrClass, rrTtl, rrLen;
NS_GET16(rrType,p);
NS_GET16(rrClass,p);
NS_GET32(rrTtl,p);
NS_GET16(rrLen,p);
// Remember current pointer and skip to next answer
unsigned char* l = p;
p += rrLen;
// Skip non SRV answers
if (rrType != (int)ns_t_srv)
continue;
// Now get record priority, weight, port, label
int prio, weight, port;
NS_GET16(prio,l);
NS_GET16(weight,l);
NS_GET16(port,l);
n = dn_expand(buf,e,l,name,sizeof(name));
if ((n <= 0) || (n > NS_MAXLABEL))
break;
insertRecord(result,new SrvRecord(rrTtl,prio,weight,name,port),false,"srvQuery");
YIGNORE(rrClass);
}
#endif
return printResult(Srv,code,dname,result,error);
}
// Make a NAPTR query
int Resolver::naptrQuery(const char* dname, ObjList& result, String* error)
{
int code = 0;
XDebug(DebugAll,"Starting %s query for '%s'",lookup(Naptr,s_types),dname);
#ifdef _WINDOWS
DNS_RECORD* naptr = 0;
if (available(Naptr))
code = (int)::DnsQuery_UTF8(dname,DNS_TYPE_NAPTR,DNS_QUERY_STANDARD,NULL,&naptr,NULL);
if (code == ERROR_SUCCESS) {
for (DNS_RECORD* dr = naptr; dr; dr = dr->pNext) {
if (dr->wType != DNS_TYPE_NAPTR)
continue;
// version >= 6: DnsQuery will set the fields in DNS_NAPTR_DATA
// else: DnsQuery puts the raw response
if (s_winVer.major() >= 6) {
#ifdef HAVE_DNS_NAPTR_DATA
if (dr->wDataLength != sizeof(DNS_NAPTR_DATA))
continue;
DNS_NAPTR_DATA& d = dr->Data.NAPTR;
insertRecord(result,new NaptrRecord(dr->dwTtl,d.wOrder,d.wPreference,d.pFlags,
d.pService,d.pRegularExpression,d.pReplacement),true,"naptrQuery");
#endif
continue;
}
int len = dr->wDataLength - 4;
if (len <= 0)
continue;
unsigned char* b = (unsigned char*)&dr->Data;
int ord = (b[0] << 8) | b[1];
int pr = (b[2] << 8) | b[3];
unsigned char* tmp = new unsigned char[len + 1];
::memcpy(tmp,b + 4,len);
tmp[len] = 0;
unsigned char* buf = tmp;
unsigned char* end = buf + len;
if (!buf)
continue;
char fla[DNS_MAX_NAME_BUFFER_LENGTH];
char ser[DNS_MAX_NAME_BUFFER_LENGTH];
char reg[DNS_MAX_NAME_BUFFER_LENGTH];
buf += dn_string(end,buf,fla,sizeof(fla));;
buf += dn_string(end,buf,ser,sizeof(ser));
buf += dn_string(end,buf,reg,sizeof(reg));
insertRecord(result,new NaptrRecord(dr->dwTtl,ord,pr,fla,ser,reg,0),true,"naptrQuery");
}
}
else if (error)
Thread::errorString(*error,code);
if (naptr)
::DnsRecordListFree(naptr,DnsFreeRecordList);
#elif defined(__RES)
unsigned char buf[2048];
int r,q,a;
unsigned char *p, *e;
r = res_query(dname,ns_c_in,ns_t_naptr,buf,sizeof(buf));
if ((r < 0) || (r > (int)sizeof(buf))) {
code = h_errno;
if (error)
*error = hstrerror(code);
return printResult(Naptr,code,dname,result,error);
}
p = buf+NS_QFIXEDSZ;
NS_GET16(q,p);
NS_GET16(a,p);
XDebug(DebugAll,"Resolver::naptrQuery(%s) questions: %d, answers: %d",dname,q,a);
p = buf + NS_HFIXEDSZ;
e = buf + r;
for (; q > 0; q--) {
int n = dn_skipname(p,e);
if (n < 0)
return printResult(Naptr,code,dname,result,error);
p += (n + NS_QFIXEDSZ);
}
XDebug(DebugAll,"Resolver::naptrQuery(%s) skipped questions",dname);
for (; a > 0; a--) {
int ty,cl,sz;
long int tt;
char name[NS_MAXLABEL+1];
unsigned char* l;
int n = dn_expand(buf,e,p,name,sizeof(name));
if ((n <= 0) || (n > NS_MAXLABEL))
break;
buf[n] = 0;
p += n;
NS_GET16(ty,p);
NS_GET16(cl,p);
NS_GET32(tt,p);
NS_GET16(sz,p);
XDebug(DebugAll,"Resolver::naptrQuery(%s) found '%s' type %d size %d",dname,name,ty,sz);
l = p;
p += sz;
if (ty != ns_t_naptr)
continue;
int ord,pr;
char fla[NS_MAXSTRING+1];
char ser[NS_MAXSTRING+1];
char reg[NS_MAXSTRING+1];
char rep[NS_MAXLABEL+1];
NS_GET16(ord,l);
NS_GET16(pr,l);
n = dn_string(e,l,fla,sizeof(fla));
l += n;
n = dn_string(e,l,ser,sizeof(ser));
l += n;
n = dn_string(e,l,reg,sizeof(reg));
l += n;
n = dn_expand(buf,e,l,rep,sizeof(rep));
l += n;
insertRecord(result,new NaptrRecord(tt,ord,pr,fla,ser,reg,rep),true,"naptrQuery");
YIGNORE(cl);
}
#endif
return printResult(Naptr,code,dname,result,error);
}
// Make an A query
int Resolver::a4Query(const char* dname, ObjList& result, String* error)
{
int code = 0;
XDebug(DebugAll,"Starting %s query for '%s'",lookup(A4,s_types),dname);
#ifdef _WINDOWS
DNS_RECORD* adr = 0;
code = (int)::DnsQuery_UTF8(dname,DNS_TYPE_A,DNS_QUERY_STANDARD,NULL,&adr,NULL);
if (code == ERROR_SUCCESS) {
for (DNS_RECORD* dr = adr; dr; dr = dr->pNext) {
if (dr->wType != DNS_TYPE_A || dr->wDataLength != sizeof(DNS_A_DATA))
continue;
SocketAddr addr(SocketAddr::IPv4,&dr->Data.A.IpAddress);
result.append(new TxtRecord(dr->dwTtl,addr.host()));
}
}
else if (error)
Thread::errorString(*error,code);
if (adr)
::DnsRecordListFree(adr,DnsFreeRecordList);
#elif defined(__RES)
unsigned char buf[512];
int r = res_query(dname,ns_c_in,ns_t_a,buf,sizeof(buf));
if (r <= 0 || r > (int)sizeof(buf)) {
if (r) {
code = h_errno;
if (error)
*error = hstrerror(code);
}
return printResult(A4,code,dname,result,error);
}
int queryCount = 0;
int answerCount = 0;
unsigned char* p = buf + NS_QFIXEDSZ;
unsigned char* e = buf + r;
NS_GET16(queryCount,p);
NS_GET16(answerCount,p);
p = buf + NS_HFIXEDSZ;
// Skip queries
for (; queryCount > 0; queryCount--) {
int n = dn_skipname(p,e);
if (n < 0)
break;
p += (n + NS_QFIXEDSZ);
}
for (int i = 0; i < answerCount; i++) {
char name[NS_MAXLABEL + 1];
// Skip this answer's query
int n = dn_expand(buf,e,p,name,sizeof(name));
if ((n <= 0) || (n > NS_MAXLABEL))
break;
buf[n] = 0;
p += n;
// Get record type, class, ttl, length
int rrType, rrClass, rrTtl, rrLen;
NS_GET16(rrType,p);
NS_GET16(rrClass,p);
NS_GET32(rrTtl,p);
NS_GET16(rrLen,p);
// Remember current pointer and skip to next answer
unsigned char* l = p;
p += rrLen;
// Skip non A answers
if (rrType != (int)ns_t_a)
continue;
// Now get record address
SocketAddr addr(SocketAddr::IPv4,l);
result.append(new TxtRecord(rrTtl,addr.host()));
YIGNORE(rrClass);
}
#endif
return printResult(A4,code,dname,result,error);
}
// Make an AAAA query
int Resolver::a6Query(const char* dname, ObjList& result, String* error)
{
int code = 0;
XDebug(DebugAll,"Starting %s query for '%s'",lookup(A6,s_types),dname);
if (!available(A6))
return printResult(A6,code,dname,result,error);
#ifdef _WINDOWS
DNS_RECORD* adr = 0;
code = (int)::DnsQuery_UTF8(dname,DNS_TYPE_AAAA,DNS_QUERY_STANDARD,NULL,&adr,NULL);
if (code == ERROR_SUCCESS) {
for (DNS_RECORD* dr = adr; dr; dr = dr->pNext) {
if (dr->wType != DNS_TYPE_AAAA || dr->wDataLength != sizeof(DNS_AAAA_DATA))
continue;
SocketAddr addr(SocketAddr::IPv6,&dr->Data.AAAA.Ip6Address);
result.append(new TxtRecord(dr->dwTtl,addr.host()));
}
}
else if (error)
Thread::errorString(*error,code);
if (adr)
::DnsRecordListFree(adr,DnsFreeRecordList);
#elif defined(__RES)
unsigned char buf[512];
int r = res_query(dname,ns_c_in,ns_t_aaaa,buf,sizeof(buf));
if (r <= 0 || r > (int)sizeof(buf)) {
if (r) {
code = h_errno;
if (error)
*error = hstrerror(code);
}
return printResult(A6,code,dname,result,error);
}
int queryCount = 0;
int answerCount = 0;
unsigned char* p = buf + NS_QFIXEDSZ;
unsigned char* e = buf + r;
NS_GET16(queryCount,p);
NS_GET16(answerCount,p);
p = buf + NS_HFIXEDSZ;
// Skip queries
for (; queryCount > 0; queryCount--) {
int n = dn_skipname(p,e);
if (n < 0)
break;
p += (n + NS_QFIXEDSZ);
}
for (int i = 0; i < answerCount; i++) {
char name[NS_MAXLABEL + 1];
// Skip this answer's query
int n = dn_expand(buf,e,p,name,sizeof(name));
if ((n <= 0) || (n > NS_MAXLABEL))
break;
buf[n] = 0;
p += n;
// Get record type, class, ttl, length
int rrType, rrClass, rrTtl, rrLen;
NS_GET16(rrType,p);
NS_GET16(rrClass,p);
NS_GET32(rrTtl,p);
NS_GET16(rrLen,p);
// Remember current pointer and skip to next answer
unsigned char* l = p;
p += rrLen;
// Skip non AAAA answers
if (rrType != (int)ns_t_aaaa)
continue;
// Now get record address
SocketAddr addr(SocketAddr::IPv6,l);
result.append(new TxtRecord(rrTtl,addr.host()));
YIGNORE(rrClass);
}
#endif
return printResult(A6,code,dname,result,error);
}
// Make a TXT query
int Resolver::txtQuery(const char* dname, ObjList& result, String* error)
{
int code = 0;
XDebug(DebugAll,"Starting %s query for '%s'",lookup(Txt,s_types),dname);
#ifdef _WINDOWS
DNS_RECORD* adr = 0;
code = (int)::DnsQuery_UTF8(dname,DNS_TYPE_TEXT,DNS_QUERY_STANDARD,NULL,&adr,NULL);
if (code == ERROR_SUCCESS) {
for (DNS_RECORD* dr = adr; dr; dr = dr->pNext) {
if (dr->wType != DNS_TYPE_TEXT || dr->wDataLength < sizeof(DNS_TXT_DATA))
continue;
DNS_TXT_DATA& d = dr->Data.TXT;
for (DWORD i = 0; i < d.dwStringCount; i++)
result.append(new TxtRecord(dr->dwTtl,d.pStringArray[i]));
}
}
else if (error)
Thread::errorString(*error,code);
if (adr)
::DnsRecordListFree(adr,DnsFreeRecordList);
#elif defined(__RES)
unsigned char buf[512];
int r = res_query(dname,ns_c_in,ns_t_txt,buf,sizeof(buf));
if (r <= 0 || r > (int)sizeof(buf)) {
if (r) {
code = h_errno;
if (error)
*error = hstrerror(code);
}
return printResult(Txt,code,dname,result,error);
}
int queryCount = 0;
int answerCount = 0;
unsigned char* p = buf + NS_QFIXEDSZ;
unsigned char* e = buf + r;
NS_GET16(queryCount,p);
NS_GET16(answerCount,p);
p = buf + NS_HFIXEDSZ;
// Skip queries
for (; queryCount > 0; queryCount--) {
int n = dn_skipname(p,e);
if (n < 0)
break;
p += (n + NS_QFIXEDSZ);
}
for (int i = 0; i < answerCount; i++) {
char name[NS_MAXLABEL + 1];
// Skip this answer's query
int n = dn_expand(buf,e,p,name,sizeof(name));
if ((n <= 0) || (n > NS_MAXLABEL))
break;
buf[n] = 0;
p += n;
// Get record type, class, ttl, length
int rrType, rrClass, rrTtl, rrLen;
NS_GET16(rrType,p);
NS_GET16(rrClass,p);
NS_GET32(rrTtl,p);
NS_GET16(rrLen,p);
// Remember current pointer and skip to next answer
unsigned char* l = p;
p += rrLen;
// Skip non TXT answers
if (rrType != (int)ns_t_txt)
continue;
// Now get record address
char txt[NS_MAXSTRING+1];
dn_string(e,l,txt,sizeof(txt));
result.append(new TxtRecord(rrTtl,txt));
YIGNORE(rrClass);
}
#endif
return printResult(Txt,code,dname,result,error);
}
/* vi: set ts=8 sw=4 sts=4 noet: */