Added Resolver class to engine. Use it in jabber library.
git-svn-id: http://voip.null.ro/svn/yate@4548 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
a2b2595631
commit
b6355eecd6
|
@ -24,7 +24,7 @@ EINC := $(CINC) @top_srcdir@/yatengine.h
|
|||
PINC := $(EINC) @top_srcdir@/yatephone.h
|
||||
CLINC:= $(PINC) @top_srcdir@/yatecbase.h
|
||||
LIBS :=
|
||||
CLSOBJS := TelEngine.o ObjList.o HashList.o Mutex.o Thread.o Socket.o \
|
||||
CLSOBJS := TelEngine.o ObjList.o HashList.o Mutex.o Thread.o Socket.o Resolver.o \
|
||||
String.o DataBlock.o NamedList.o Evaluator.o \
|
||||
URI.o Mime.o Array.o Iterator.o \
|
||||
YMD5.o YSHA1.o Base64.o Cipher.o Compressor.o
|
||||
|
@ -91,6 +91,9 @@ DataFormat.o: @srcdir@/DataFormat.cpp $(MKDEPS) $(PINC)
|
|||
Socket.o: @srcdir@/Socket.cpp $(MKDEPS) $(CINC)
|
||||
$(COMPILE) @FDSIZE_HACK@ @NETDB_FLAGS@ -c $<
|
||||
|
||||
Resolver.o: @srcdir@/Resolver.cpp $(MKDEPS) $(CINC)
|
||||
$(COMPILE) @RESOLV_INC@ -c $<
|
||||
|
||||
Mutex.o: @srcdir@/Mutex.cpp $(MKDEPS) $(CINC)
|
||||
$(COMPILE) @MUTEX_HACK@ -c $<
|
||||
|
||||
|
@ -116,4 +119,4 @@ Makefile: @srcdir@/Makefile.in $(MKDEPS)
|
|||
cd .. && ./config.status
|
||||
|
||||
../$(YLIB): $(LIBOBJS) $(LIBS)
|
||||
$(LINK) -o $@ $(SONAME_OPT)$(YLIB) $^ $(LIBTHR) $(LIBAUX)
|
||||
$(LINK) -o $@ $(SONAME_OPT)$(YLIB) $^ $(LIBTHR) $(LIBAUX) @RESOLV_LIB@
|
||||
|
|
|
@ -0,0 +1,588 @@
|
|||
/**
|
||||
* 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-2011 Null Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
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 },
|
||||
{ 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(__NAMESER)
|
||||
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("order=",sep) << m_order;
|
||||
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) order=%d pref=%d",rec,rec->order(),rec->pref());
|
||||
ObjList* o = list.skipNull();
|
||||
for (; o; o = o->skipNext()) {
|
||||
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
|
||||
list.append(rec);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 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->order(),rec->pref(),rec->address(),rec->port()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NaptrRecord::NaptrRecord(int ord, int pref, const char* flags, const char* serv,
|
||||
const char* regexp, const char* next)
|
||||
: DnsRecord(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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
#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(__NAMESER)
|
||||
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(__NAMESER)
|
||||
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)
|
||||
{
|
||||
if (type == Srv)
|
||||
return srvQuery(dname,result,error);
|
||||
if (type == Naptr)
|
||||
return naptrQuery(dname,result,error);
|
||||
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(d.wPriority,d.wWeight,
|
||||
d.pNameTarget,d.wPort),false,"srvQuery");
|
||||
}
|
||||
}
|
||||
else if (error)
|
||||
Thread::errorString(*error,code);
|
||||
if (srv)
|
||||
::DnsRecordListFree(srv,DnsFreeRecordList);
|
||||
#elif defined(__NAMESER)
|
||||
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(prio,weight,name,port),false,"srvQuery");
|
||||
}
|
||||
#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(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(ord,pr,fla,ser,reg,0),true,"naptrQuery");
|
||||
}
|
||||
}
|
||||
else if (error)
|
||||
Thread::errorString(*error,code);
|
||||
if (naptr)
|
||||
::DnsRecordListFree(naptr,DnsFreeRecordList);
|
||||
#elif defined(__NAMESER)
|
||||
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(ord,pr,fla,ser,reg,rep),true,"naptrQuery");
|
||||
}
|
||||
#endif
|
||||
return printResult(Naptr,code,dname,result,error);
|
||||
}
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -59,7 +59,7 @@ Makefile: @srcdir@/Makefile.in ../../config.status
|
|||
cd ../.. && ./config.status
|
||||
|
||||
../../$(LIBD_VER): $(OBJS) $(YXML)
|
||||
$(LINK) -o $@ $(SONAME_OPT)$(LIBD_VER) $^ $(YATELIBS) @RESOLV_LIB@
|
||||
$(LINK) -o $@ $(SONAME_OPT)$(LIBD_VER) $^ $(YATELIBS)
|
||||
|
||||
../../$(LIBD_DEV): ../../$(LIBD_VER)
|
||||
cd ../.. && ln -sf $(LIBD_VER) $(LIBD_DEV)
|
||||
|
|
|
@ -635,7 +635,9 @@ void JBConnect::connect()
|
|||
// Start connecting timeout
|
||||
if (!notifyConnecting(true,true))
|
||||
return;
|
||||
int code = Resolver::srvQuery(query,m_srvs,&error);
|
||||
int code = 0;
|
||||
if (Resolver::init())
|
||||
code = Resolver::srvQuery(query,m_srvs,&error);
|
||||
// Stop the timeout if not exiting
|
||||
if (exiting(sock) || !notifyConnecting(false,true)) {
|
||||
terminated(0,false);
|
||||
|
@ -654,7 +656,7 @@ void JBConnect::connect()
|
|||
ObjList* o = 0;
|
||||
while (0 != (o = m_srvs.skipNull())) {
|
||||
SrvRecord* rec = static_cast<SrvRecord*>(o->get());
|
||||
sock = connect(*rec,rec->m_port,stop);
|
||||
sock = connect(rec->address(),rec->port(),stop);
|
||||
o->remove();
|
||||
if (sock || stop || exiting(sock)) {
|
||||
terminated(sock,false);
|
||||
|
|
|
@ -27,149 +27,6 @@
|
|||
|
||||
using namespace TelEngine;
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <windns.h>
|
||||
#else
|
||||
#include <resolv.h>
|
||||
#include <arpa/nameser.h>
|
||||
#endif
|
||||
// Insert a SrvRecord into a list in the proper location
|
||||
void SrvRecord::insert(ObjList& list, SrvRecord* rec)
|
||||
{
|
||||
XDebug(DebugAll,"SrvRecord::insert(%s port=%d prio=%d weight=%d) [%p]",
|
||||
rec->c_str(),rec->m_port,rec->m_priority,rec->m_weight,rec);
|
||||
// NOTE: SRV records with the same priority can be returned by the query
|
||||
// Their relative order is given by the weight value
|
||||
// Lower priority number means a higher priority
|
||||
// Higher weight number means a higher priority
|
||||
// Append items with equal priority and weight in the order they arrive
|
||||
for (ObjList* o = list.skipNull(); o; o = o->skipNext()) {
|
||||
SrvRecord* crt = static_cast<SrvRecord*>(o->get());
|
||||
// Skip lower priority values
|
||||
if (rec->m_priority > crt->m_priority)
|
||||
continue;
|
||||
if (rec->m_priority < crt->m_priority)
|
||||
o->insert(rec);
|
||||
else {
|
||||
// Equal priority: skip until less weight or different priority
|
||||
for (; o; o = o->skipNext()) {
|
||||
SrvRecord* crt = static_cast<SrvRecord*>(o->get());
|
||||
if (crt->m_priority != rec->m_priority || crt->m_weight < rec->m_weight)
|
||||
break;
|
||||
}
|
||||
if (o)
|
||||
o->insert(rec);
|
||||
else
|
||||
list.append(rec);
|
||||
}
|
||||
return;
|
||||
}
|
||||
list.append(rec);
|
||||
}
|
||||
|
||||
// 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,rec->m_port,rec->m_priority,rec->m_weight));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Make a SRV query
|
||||
int Resolver::srvQuery(const char* query, ObjList& result, String* error)
|
||||
{
|
||||
int code = 0;
|
||||
XDebug(DebugAll,"Starting SRV query for '%s'",query);
|
||||
#ifdef _WINDOWS
|
||||
DNS_RECORD* srv = 0;
|
||||
code = (int)::DnsQuery_UTF8(query,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;
|
||||
SrvRecord::insert(result,new SrvRecord(dr->Data.SRV.pNameTarget,dr->Data.SRV.wPort,
|
||||
dr->Data.SRV.wPriority,dr->Data.SRV.wWeight));
|
||||
}
|
||||
}
|
||||
else if (error)
|
||||
Thread::errorString(*error,code);
|
||||
if (srv)
|
||||
::DnsRecordListFree(srv,DnsFreeRecordList);
|
||||
#elif defined(__NAMESER)
|
||||
unsigned char buf[512];
|
||||
int r = res_query(query,ns_c_in,ns_t_srv,buf,sizeof(buf));
|
||||
if (r < 0) {
|
||||
code = h_errno;
|
||||
if (error)
|
||||
*error = hstrerror(code);
|
||||
}
|
||||
if (!code && r > 0 && r <= (int)sizeof(buf)) {
|
||||
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;
|
||||
SrvRecord::insert(result,new SrvRecord(name,port,prio,weight));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef XDEBUG
|
||||
if (!code) {
|
||||
String s;
|
||||
for (ObjList* o = result.skipNull(); o; o = o->skipNext()) {
|
||||
SrvRecord* rec = static_cast<SrvRecord*>(o->get());
|
||||
s << " " << *rec;
|
||||
s << " (prio=" << rec->m_priority << " port=" << rec->m_port;
|
||||
s << " weight=" << rec->m_weight << ")";
|
||||
}
|
||||
Debug(DebugAll,"SRV query for '%s' got %d records%s",query,result.count(),s.safe());
|
||||
}
|
||||
else
|
||||
Debug(DebugNote,"SRV query for '%s' failed with code %d",query,code);
|
||||
#endif
|
||||
return code;
|
||||
}
|
||||
|
||||
static const JabberID s_emptyJid;
|
||||
static String s_auth[] = {"password", "auth", ""};
|
||||
|
||||
|
|
|
@ -66,51 +66,6 @@ class XMPPUtils; // Utilities
|
|||
class XMPPDirVal; // Direction flags
|
||||
class XmlElementOut; // An outgoing xml element
|
||||
|
||||
/**
|
||||
* This class holds a SRV record returned by a query
|
||||
* The String holds the domain/ip
|
||||
* @short A SRV record
|
||||
*/
|
||||
class YJABBER_API SrvRecord : public String
|
||||
{
|
||||
public:
|
||||
inline SrvRecord(const char* name, int port, int prio, int weight)
|
||||
: String(name), m_port(port), m_priority(prio), m_weight(weight)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Insert a SrvRecord into a list in the proper location
|
||||
* @param list Destination list
|
||||
* @param rec The item to insert
|
||||
*/
|
||||
static void insert(ObjList& list, SrvRecord* rec);
|
||||
|
||||
/**
|
||||
* Copy a SrvRecord list into another one
|
||||
* @param dest Destination list
|
||||
* @param src Source list
|
||||
*/
|
||||
static void copy(ObjList& dest, const ObjList& src);
|
||||
|
||||
int m_port;
|
||||
int m_priority;
|
||||
int m_weight;
|
||||
};
|
||||
|
||||
class YJABBER_API Resolver
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Make a SRV query
|
||||
* @param query The query content
|
||||
* @param result List of resulting SrvRecord items
|
||||
* @param error Optional string to be filled with error string
|
||||
* @return 0 on success, error code otherwise (h_errno value on Linux)
|
||||
*/
|
||||
static int srvQuery(const char* query, ObjList& result, String* error = 0);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Implements a String array set from an already allocated
|
||||
* @short A String array
|
||||
|
|
250
yateclass.h
250
yateclass.h
|
@ -6359,6 +6359,256 @@ public:
|
|||
virtual bool setPayload(u_int32_t payload) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class holds a DNS (resolver) record
|
||||
* @short A DNS record
|
||||
*/
|
||||
class YATE_API DnsRecord : public GenObject
|
||||
{
|
||||
YCLASS(DnsRecord,GenObject)
|
||||
YNOCOPY(DnsRecord);
|
||||
public:
|
||||
/**
|
||||
* Build a DNS record
|
||||
* @param order Record order (priority)
|
||||
* @param pref Record preference
|
||||
*/
|
||||
inline DnsRecord(int order, int pref)
|
||||
: m_order(order), m_pref(pref)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
inline DnsRecord()
|
||||
: m_order(0), m_pref(0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Retrieve the record order
|
||||
* @return Record order
|
||||
*/
|
||||
inline int order() const
|
||||
{ return m_order; }
|
||||
|
||||
/**
|
||||
* Retrieve the record preference
|
||||
* @return Record preference
|
||||
*/
|
||||
inline int pref() const
|
||||
{ return m_pref; }
|
||||
|
||||
/**
|
||||
* Dump a record for debug purposes
|
||||
* @param buf Destination buffer
|
||||
* @param sep Fields separator
|
||||
*/
|
||||
virtual void dump(String& buf, const char* sep = " ");
|
||||
|
||||
/**
|
||||
* Insert a DnsRecord into a list in the proper location given by order and preference
|
||||
* @param list Destination list
|
||||
* @param rec The item to insert
|
||||
* @param ascPref Order preference ascending
|
||||
* @return True on success, false on failure (already in the list)
|
||||
*/
|
||||
static bool insert(ObjList& list, DnsRecord* rec, bool ascPref);
|
||||
|
||||
protected:
|
||||
int m_order;
|
||||
int m_pref;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class holds a SRV (Service Location) record
|
||||
* @short A SRV record
|
||||
*/
|
||||
class YATE_API SrvRecord : public DnsRecord
|
||||
{
|
||||
YCLASS(SrvRecord,DnsRecord)
|
||||
YNOCOPY(SrvRecord);
|
||||
public:
|
||||
/**
|
||||
* Build a SRV record
|
||||
* @param prio Record priority (order)
|
||||
* @param weight Record weight (preference)
|
||||
* @param addr Record address
|
||||
* @param port Record port
|
||||
*/
|
||||
inline SrvRecord(int prio, int weight, const char* addr, int port)
|
||||
: DnsRecord(prio,weight), m_address(addr), m_port(port)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Retrieve the record address
|
||||
* @return Record address
|
||||
*/
|
||||
inline const String& address() const
|
||||
{ return m_address; }
|
||||
|
||||
/**
|
||||
* Retrieve the record port
|
||||
* @return Record port
|
||||
*/
|
||||
inline int port() const
|
||||
{ return m_port; }
|
||||
|
||||
/**
|
||||
* Dump this record for debug purposes
|
||||
* @param buf Destination buffer
|
||||
* @param sep Fields separator
|
||||
*/
|
||||
virtual void dump(String& buf, const char* sep = " ");
|
||||
|
||||
/**
|
||||
* Copy a SrvRecord list into another one
|
||||
* @param dest Destination list
|
||||
* @param src Source list
|
||||
*/
|
||||
static void copy(ObjList& dest, const ObjList& src);
|
||||
|
||||
protected:
|
||||
String m_address;
|
||||
int m_port;
|
||||
|
||||
private:
|
||||
SrvRecord() {} // No default contructor
|
||||
};
|
||||
|
||||
/**
|
||||
* This class holds a NAPTR (Naming Authority Pointer) record
|
||||
* @short A NAPTR record
|
||||
*/
|
||||
class YATE_API NaptrRecord : public DnsRecord
|
||||
{
|
||||
YCLASS(NaptrRecord,DnsRecord)
|
||||
YNOCOPY(NaptrRecord);
|
||||
public:
|
||||
/**
|
||||
* Build a NAPTR record
|
||||
* @param ord Record order
|
||||
* @param pref Record preference
|
||||
* @param flags Interpretation flags
|
||||
* @param serv Available services
|
||||
* @param regexp Substitution expression
|
||||
* @param next Next name to query
|
||||
*/
|
||||
NaptrRecord(int ord, int pref, const char* flags, const char* serv,
|
||||
const char* regexp, const char* next);
|
||||
|
||||
/**
|
||||
* Replace the enclosed template in a given string if matching
|
||||
* the substitution expression
|
||||
* @param str String to replace
|
||||
* @return True on success
|
||||
*/
|
||||
bool replace(String& str);
|
||||
|
||||
/**
|
||||
* Dump this record for debug purposes
|
||||
* @param buf Destination buffer
|
||||
* @param sep Fields separator
|
||||
*/
|
||||
virtual void dump(String& buf, const char* sep = " ");
|
||||
|
||||
/**
|
||||
* Retrieve record interpretation flags
|
||||
* @return Record interpretation flags
|
||||
*/
|
||||
inline const String& flags() const
|
||||
{ return m_flags; }
|
||||
|
||||
/**
|
||||
* Retrieve available services
|
||||
* @return Available services
|
||||
*/
|
||||
inline const String& serv() const
|
||||
{ return m_service; }
|
||||
|
||||
/**
|
||||
* Retrieve the next domain name to query
|
||||
* @return The next domain to query
|
||||
*/
|
||||
inline const String& nextName() const
|
||||
{ return m_next; }
|
||||
|
||||
protected:
|
||||
String m_flags;
|
||||
String m_service;
|
||||
Regexp m_regmatch;
|
||||
String m_template;
|
||||
String m_next;
|
||||
|
||||
private:
|
||||
NaptrRecord() {} // No default contructor
|
||||
};
|
||||
|
||||
/**
|
||||
* This class offers DNS query services
|
||||
* @short DNS services
|
||||
*/
|
||||
class YATE_API Resolver
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Resolver handled types
|
||||
*/
|
||||
enum Type {
|
||||
Unknown,
|
||||
Srv, // SRV (Service Location)
|
||||
Naptr, // NAPTR (Naming Authority Pointer)
|
||||
};
|
||||
|
||||
/**
|
||||
* Runtime check for resolver availability
|
||||
* @param type Optional type to check. Set it to Unknown (default) to check
|
||||
* general resolver availability
|
||||
* @return True if the resolver is available on current platform
|
||||
*/
|
||||
static bool available(Type type = Unknown);
|
||||
|
||||
/**
|
||||
* Initialize the resolver in the current thread
|
||||
* @param timeout Query timeout. Negative to use default
|
||||
* @param retries The number of query retries. Negative to use default
|
||||
* @return True on success
|
||||
*/
|
||||
static bool init(int timeout = -1, int retries = -1);
|
||||
|
||||
/**
|
||||
* Make a query
|
||||
* @param type Query type as enumeration
|
||||
* @param dname Domain to query
|
||||
* @param result List of resulting record items
|
||||
* @param error Optional string to be filled with error string
|
||||
* @return 0 on success, error code otherwise (h_errno value on Linux)
|
||||
*/
|
||||
static int query(Type type, const char* dname, ObjList& result, String* error = 0);
|
||||
|
||||
/**
|
||||
* Make a SRV (Service Location) query
|
||||
* @param dname Domain to query
|
||||
* @param result List of resulting SrvRecord items
|
||||
* @param error Optional string to be filled with error string
|
||||
* @return 0 on success, error code otherwise (h_errno value on Linux)
|
||||
*/
|
||||
static int srvQuery(const char* dname, ObjList& result, String* error = 0);
|
||||
|
||||
/**
|
||||
* Make a NAPTR (Naming Authority Pointer) query
|
||||
* @param dname Domain to query
|
||||
* @param result List of resulting NaptrRecord items
|
||||
* @param error Optional string to be filled with error string
|
||||
* @return 0 on success, error code otherwise (h_errno value on Linux)
|
||||
*/
|
||||
static int naptrQuery(const char* dname, ObjList& result, String* error = 0);
|
||||
|
||||
/**
|
||||
* Resolver type names
|
||||
*/
|
||||
static const TokenDict s_types[];
|
||||
};
|
||||
|
||||
/**
|
||||
* The Cipher class provides an abstraction for data encryption classes
|
||||
* @short An abstract cipher
|
||||
|
|
Loading…
Reference in New Issue