Files
resiprocate/tfm/TfmDns.cxx
2026-01-08 17:50:09 +01:00

509 lines
13 KiB
C++

#include <fstream>
#include "rutil/DnsUtil.hxx"
#include "TfmDns.hxx"
#include "rutil/WinLeakCheck.hxx"
#include <ares.h>
#include <ares_dns.h>
#if !defined WIN32 && !defined __CYGWIN__ && !defined USE_ARES
#include <resolv.h>
#endif
using namespace resip;
using namespace std;
unsigned short TfmDns::mTransactionId = 0;
int
TfmDns::init(const std::vector<GenericIPAddress>& additionalNameservers,
AfterSocketCreationFuncPtr socketfunc,
int dnsTimeout,
int dnsTries,
unsigned int features)
{
return Success;
}
int
TfmDns::init(int dnsTimeout, int dnsTries, unsigned int features)
{
return Success;
}
TfmDns::TfmDns()
{
//setup();
}
TfmDns::~TfmDns()
{
}
bool
TfmDns::requiresProcess()
{
return true;
}
void
TfmDns::buildFdSet(fd_set& read, fd_set& write, int& size)
{
}
void
TfmDns::process(fd_set& read, fd_set& write)
{
}
void
TfmDns::lookup(const char* target, unsigned short type, ExternalDnsHandler* handler, void* userData)
{
switch (type)
{
case 1:
lookupA(target, handler, userData);
break;
case 33:
lookupSrv(target, handler, userData);
break;
default:
memset(mResponse, 0, sizeof(mResponse)/sizeof(char));
handler->handleDnsRaw(ExternalDnsRawResult(ARES_ENOTIMP, mResponse, 0, userData));
break;
}
}
void
TfmDns::lookupA(const char* target, ExternalDnsHandler* handler, void* userData)
{
static list<Host> Empty;
HostMap::iterator it = mHosts.find(target);
if (it == mHosts.end())
{
makeAResponse(target, Empty);
handler->handleDnsRaw(ExternalDnsRawResult(ARES_ENOTFOUND, mResponse, mResponseLength, userData));
}
else
{
makeAResponse(target, it->second);
handler->handleDnsRaw(ExternalDnsRawResult(mResponse, mResponseLength, userData));
}
}
void
TfmDns::lookupSrv(const char* target, ExternalDnsHandler* handler, void* userData)
{
static list<SRV> Empty;
SRVMap::iterator it = mSRVs.find(target);
if (it == mSRVs.end())
{
makeSrvResponse(target, Empty);
handler->handleDnsRaw(ExternalDnsRawResult(ARES_ENOTFOUND, mResponse, mResponseLength, userData));
}
else
{
makeSrvResponse(target, it->second);
handler->handleDnsRaw(ExternalDnsRawResult(mResponse, mResponseLength, userData));
}
}
// Pre-condiiton: total length doesn't exceed 512 bytes and no bad names.
void
TfmDns::makeSrvResponse(const char* target, const std::list<SRV>& srvs)
{
memset(mResponse, 0, sizeof(mResponse)/sizeof(char));
int nameLen = computeLength(target);
mResponseLength = (nameLen * (1 + (int)srvs.size())) + HFIXEDSZ + QFIXEDSZ + (RRFIXEDSZ * (int)srvs.size());
for (list<SRV>::const_iterator it = srvs.begin(); it != srvs.end(); ++it)
{
mResponseLength += computeSrvDataLength(it->mTarget);
}
int len;
unsigned char *q;
const char *p;
/* Set up the header. */
q = mResponse;
DNS_HEADER_SET_QID(q, getNextTransactionId());
DNS_HEADER_SET_QR(q, 1);
DNS_HEADER_SET_OPCODE(q, QUERY);
DNS_HEADER_SET_TC(q, 0);
DNS_HEADER_SET_RCODE(q, 0);
DNS_HEADER_SET_QDCOUNT(q, 1);
DNS_HEADER_SET_ANCOUNT(q, srvs.size());
DNS_HEADER_SET_NSCOUNT(q, 0);
DNS_HEADER_SET_ARCOUNT(q, 0);
/* Start writing out the name after the header. */
q += HFIXEDSZ;
const char* name = target;
while (*name)
{
/* Count the number of bytes in this label. */
len = 0;
for (p = name; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
len++;
}
/* Encode the length and copy the data. */
*q++ = len;
for (p = name; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
*q++ = *p;
}
/* Go to the next label and repeat, unless we hit the end. */
if (!*p)
break;
name = p + 1;
}
/* Add the zero-length label at the end. */
*q++ = 0;
/* Finish off the question with the type and class. */
DNS_QUESTION_SET_TYPE(q, 33);
DNS_QUESTION_SET_CLASS(q, C_IN);
q += QFIXEDSZ;
// start writing out the answers
for (list<SRV>::const_iterator it = srvs.begin(); it != srvs.end(); ++it)
{
name = target;
while (*name)
{
/* Count the number of bytes in this label. */
len = 0;
for (p = name; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
len++;
}
/* Encode the length and copy the data. */
*q++ = len;
for (p = name; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
*q++ = *p;
}
/* Go to the next label and repeat, unless we hit the end. */
if (!*p)
break;
name = p + 1;
}
/* Add the zero-length label at the end. */
*q++ = 0;
DNS_RR_SET_TYPE(q, 33);
DNS_RR_SET_CLASS(q, C_IN);
DNS_RR_SET_TTL(q, 3600);
int dataLen = computeSrvDataLength(it->mTarget);
DNS_RR_SET_LEN(q, dataLen);
q += RRFIXEDSZ;
// encode priority, weight, and prot.
DNS__SET16BIT(q, it->mPriority);
DNS__SET16BIT(q+2, it->mWeight);
DNS__SET16BIT(q+4, it->mPort);
q += 6;
const char* data = it->mTarget.c_str();
while (*data)
{
/* Count the number of bytes in this label. */
len = 0;
for (p = data; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
len++;
}
/* Encode the length and copy the data. */
*q++ = len;
for (p = data; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
*q++ = *p;
}
/* Go to the next label and repeat, unless we hit the end. */
if (!*p)
break;
data = p + 1;
}
/* Add the zero-length label at the end. */
*q++ = 0;
}
}
int
TfmDns::computeLength(const Data& name)
{
const char *p;
int len = 1;
for (p = name.c_str(); *p; ++p)
{
if (*p == '\\' && *(p + 1) != 0)
{
++p;
}
++len;
}
/* If there are n periods in the name, there are n + 1 labels, and
* thus n + 1 length fields, unless the name is empty or ends with a
* period. So add 1 unless name is empty or ends with a period.
*/
if (name[0] && *(p - 1) != '.')
{
++len;
}
return len;
}
void
TfmDns::makeAResponse(const char* target, const std::list<Host>& hosts)
{
memset(mResponse, 0, sizeof(mResponse)/sizeof(char));
int nameLen = computeLength(target);
static int addrLength = 4;
mResponseLength = (nameLen * (1 + (int)hosts.size())) + HFIXEDSZ + QFIXEDSZ + (RRFIXEDSZ * (int)hosts.size()) + (int)hosts.size() * addrLength;
int len;
unsigned char *q;
const char *p;
/* Set up the header. */
q = mResponse;
DNS_HEADER_SET_QID(q, getNextTransactionId());
DNS_HEADER_SET_QR(q, 1);
DNS_HEADER_SET_OPCODE(q, QUERY);
DNS_HEADER_SET_TC(q, 0);
DNS_HEADER_SET_RCODE(q, 0);
DNS_HEADER_SET_QDCOUNT(q, 1);
DNS_HEADER_SET_ANCOUNT(q, hosts.size());
DNS_HEADER_SET_NSCOUNT(q, 0);
DNS_HEADER_SET_ARCOUNT(q, 0);
/* Start writing out the name after the header. */
q += HFIXEDSZ;
const char* name = target;
while (*name)
{
/* Count the number of bytes in this label. */
len = 0;
for (p = name; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
len++;
}
/* Encode the length and copy the data. */
*q++ = len;
for (p = name; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
*q++ = *p;
}
/* Go to the next label and repeat, unless we hit the end. */
if (!*p)
break;
name = p + 1;
}
/* Add the zero-length label at the end. */
*q++ = 0;
/* Finish off the question with the type and class. */
DNS_QUESTION_SET_TYPE(q, 1);
DNS_QUESTION_SET_CLASS(q, C_IN);
q += QFIXEDSZ;
// start writing out the answers
for (list<Host>::const_iterator it = hosts.begin(); it != hosts.end(); ++it)
{
name = target;
while (*name)
{
/* Count the number of bytes in this label. */
len = 0;
for (p = name; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
len++;
}
/* Encode the length and copy the data. */
*q++ = len;
for (p = name; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
*q++ = *p;
}
/* Go to the next label and repeat, unless we hit the end. */
if (!*p)
break;
name = p + 1;
}
/* Add the zero-length label at the end. */
*q++ = 0;
DNS_RR_SET_TYPE(q, 1);
DNS_RR_SET_CLASS(q, C_IN);
DNS_RR_SET_TTL(q, 3600);
DNS_RR_SET_LEN(q, addrLength);
q += RRFIXEDSZ;
// encode the address.
in_addr addr;
DnsUtil::inet_pton(it->mIp, addr);
memcpy(q, &addr, addrLength);
q += addrLength;
}
}
int
TfmDns::computeSrvDataLength(const Data& target)
{
return computeLength(target) + 6; // 6 bytes for priority, weight, and port.
}
void
TfmDns::setup()
{
// add some SRV records
list<SRV> srvs;
SRV srv1(0, 10, 5060, "proxy1.localhost");
SRV srv2(10, 20, 5060, "proxy2.localhost");
srvs.push_back(srv1);
srvs.push_back(srv2);
mSRVs.insert(SRVMap::value_type("_sip._udp.localhost", srvs));
mSRVs.insert(SRVMap::value_type("_sip._tcp.localhost", srvs));
list<Host> hosts;
Host host1("192.168.0.8");
hosts.push_back(host1);
mHosts.insert(HostMap::value_type("proxy1.localhost", hosts));
hosts.clear();
Host host2("127.0.0.1");
hosts.push_back(host2);
mHosts.insert(HostMap::value_type("proxy2.localhost", hosts));
}
void
TfmDns::addSrvs(const Data& query, const list<SRV>& srvs)
{
SRVMap::iterator it = mSRVs.find(query);
if (it == mSRVs.end())
{
mSRVs.insert(SRVMap::value_type(query, srvs));
}
else
{
// no duplication check
it->second.insert(it->second.end(), srvs.begin(), srvs.end());
}
}
void
TfmDns::addHosts(const Data& domain, const list<Host>& hosts)
{
HostMap::iterator it = mHosts.find(domain);
if (it == mHosts.end())
{
mHosts.insert(HostMap::value_type(domain, hosts));
}
else
{
// no duplication check
it->second.insert(it->second.end(), hosts.begin(), hosts.end());
}
}
/* ====================================================================
* The Vovida Software License, Version 1.0
*
* Copyright (c) 2000-2005 Vovida Networks, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The names "VOCAL", "Vovida Open Communication Application Library",
* and "Vovida Open Communication Application Library (VOCAL)" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact vocal@vovida.org.
*
* 4. Products derived from this software may not be called "VOCAL", nor
* may "VOCAL" appear in their name, without prior written
* permission of Vovida Networks, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* ====================================================================
*
* This software consists of voluntary contributions made by Vovida
* Networks, Inc. and many individuals on behalf of Vovida Networks,
* Inc. For more information on Vovida Networks, Inc., please see
* <http://www.vovida.org/>.
*
*/