< Previous by Date Date Index Next by Date >
< Previous in Thread Thread Index Next in Thread >

Re: [reSIProcate] Is Transaction StateLessHandler is still supported or not?


Jason,

I have implemented and tested the changes to support more than ENUM DNS servers.

Here are changes:
1. added variable mNumSuffixes in DnsResult.hxx
2. modified lookup() ,  onDnsResult(), and onEnumResult() in DnsResult.cxx.

DnsResult.hxx and DnsResult.cxx are attached.
Feel free to modify or check in if you think these changes are ok.
Thanks

Frank Yuan
Emergent-Netsolutions.com
972-359-6600


Jason Fischl wrote:
Is StatelessHandler solidly tested or not?

Will it be deprecated or not?

    

StatelessHandler is deprecated.

  
#if !defined(RESIP_DNSRESULT_HXX)
#define RESIP_DNSRESULT_HXX

#include <iosfwd>
#include <set>
#include <vector>
#include <deque>
#include <map>
#include <stack>

#include "resip/stack/Tuple.hxx"
#include "resip/stack/Transport.hxx"
#include "resip/stack/Uri.hxx"
#include "rutil/HeapInstanceCounter.hxx"
#include "rutil/dns/RRVip.hxx"
#include "rutil/dns/DnsStub.hxx"

#ifdef WIN32
#include <Ws2tcpip.h>
#endif

struct hostent;

namespace resip
{
class DnsInterface;
class DnsHandler;

class DnsResult : public DnsResultSink
{
   public:
      RESIP_HeapCount(DnsResult);
      DnsResult(DnsInterface& interfaceObj, DnsStub& dns, RRVip& vip, 
DnsHandler* handler);
      virtual ~DnsResult();

      typedef enum
      {
         Available, // A result is available now
         Pending,   // More results may be pending 
         Finished,  // No more results available and none pending
         Destroyed  // the associated transaction has been deleted
      } Type;

      typedef std::vector<Data> DataVector;

      // Starts a lookup.  Has the rules for determining the transport
      // from a uri as per rfc3263 and then does a NAPTR lookup or an A
      // lookup depending on the uri
      void lookup(const Uri& uri, const std::vector<Data> &enumSuffixes);

      // Check if there are tuples available now. Will load new tuples in if
      // necessary at a lower priority. 
      Type available();
      
      // return the next tuple available for this query. The caller should have
      // checked available() before calling next()
      Tuple next();

      // whitelist the tuple returned by next().
      void success();

      // return the target of associated query
      Data target() const { return mTarget; }
      
      // Will delete this DnsResult if no pending queries are out there or wait
      // until the pending queries get responses and then delete
      void destroy();

      // Used to store a NAPTR result
      class NAPTR
      {
         public:
            NAPTR();
            // As defined by RFC3263
            bool operator<(const NAPTR& rhs) const;

            Data key; // NAPTR record key
            
            int order;
            int pref;
            Data flags;
            Data service;
            DnsNaptrRecord::RegExp regex;
            Data replacement;
      };
      
      class SRV
      {
         public:
            SRV();
            // As defined by RFC3263, RFC2782
            bool operator<(const SRV& rhs) const;
            
            Data key; // SRV record key
            
            TransportType transport;
            int priority;
            int weight;
            int cumulativeWeight; // for picking 
            int port;
            Data target;
      };

   private:
      void lookupInternal(const Uri& uri);

      // Given a transport and port from uri, return the default port to use
      int getDefaultPort(TransportType transport, int port);
      
      void lookupHost(const Data& target);
      
      // compute the cumulative weights for the SRV entries with the lowest
      // priority, then randomly pick according to RFC2782 from the entries with
      // the lowest priority based on weights. When all entries at the lowest
      // priority are chosen, pick the next lowest priority and repeat. After an
      // SRV entry is selected, remove it from mSRVResults
      SRV retrieveSRV();
      
      // Will retrieve the next SRV record and compute the prime the mResults
      // with the appropriate Tuples. 
      void primeResults();
      
      // Some utilities for parsing dns results
      static const unsigned char* skipDNSQuestion(const unsigned char *aptr,
                                                  const unsigned char *abuf,
                                                  int alen);
   private:
      DnsInterface& mInterface;
      DnsStub& mDns;
      RRVip& mVip;
      DnsHandler* mHandler;
      int mSRVCount;
      Uri mInputUri;
      bool mDoingEnum;
      
      bool mSips;
      Data mTarget;
      Data mSrvKey;
      TransportType mTransport; // current
      int mPort; // current
      Type mType;

      //Ugly hack
      Data mPassHostFromAAAAtoA;

      void transition(Type t);      
      
      // This is where the current pending (ordered) results are stored. As they
      // are retrieved by calling next(), they are popped from the front of the 
list
      std::deque<Tuple> mResults;
      
      // The best NAPTR record. Only one NAPTR record will be selected
      NAPTR mPreferredNAPTR;

      // used in determining the next SRV record to use as per rfc2782
      int mCumulativeWeight; // for current priority

      // All SRV records sorted in order of preference
      std::vector<SRV> mSRVResults;
      
      friend class DnsInterface;
      friend std::ostream& operator<<(std::ostream& strm, const DnsResult&);
      friend std::ostream& operator<<(std::ostream& strm, const 
DnsResult::SRV&);
      friend std::ostream& operator<<(std::ostream& strm, const 
DnsResult::NAPTR&);

      // DnsResultSink
      void onDnsResult(const DNSResult<DnsHostRecord>&);

#ifdef USE_IPV6
      void onDnsResult(const DNSResult<DnsAAAARecord>&);
#endif

      void onDnsResult(const DNSResult<DnsSrvRecord>&);
      void onDnsResult(const DNSResult<DnsNaptrRecord>&);
      void onDnsResult(const DNSResult<DnsCnameRecord>&);

      void onEnumResult(const DNSResult<DnsNaptrRecord>& result);
      void onNaptrResult(const DNSResult<DnsNaptrRecord>& result);
      

      typedef struct
      {
            Data domain;
            int rrType;
            Data value; // stores ip for A/AAAA, target host:port for SRV, and 
replacement for NAPTR.
      } Item;

      std::stack<Item> mCurrResultPath;
      std::stack<Item> mCurrSuccessPath;

      void clearCurrPath();
      void addToPath(const std::deque<Tuple>& results);
      void blacklistLastReturnedResult();

      Tuple mLastReturnedResult;
      bool mBlacklistLastReturnedResult;
      unsigned char mNumSuffixes;

};

std::ostream& operator<<(std::ostream& strm, const DnsResult&);
std::ostream& operator<<(std::ostream& strm, const DnsResult::SRV&);
std::ostream& operator<<(std::ostream& strm, const DnsResult::NAPTR&);

}

#endif
//  Copyright (c) 2003, Jason Fischl 
/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 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@xxxxxxxxxxx
 *
 * 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/>.
 *
 */
#if defined(HAVE_CONFIG_H)
#include "resip/stack/config.hxx"
#endif

#include <algorithm>
#include <stack>

#ifndef WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#ifndef __CYGWIN__
#  include <netinet/in.h>
#  include <arpa/nameser.h>
#  include <resolv.h>
#endif
#include <netdb.h>
#include <netinet/in.h>
#else
#include <Winsock2.h>
#include <svcguid.h>
#ifdef USE_IPV6
#include <ws2tcpip.h>
#endif
#endif

#if defined(USE_ARES)
#include "ares.h"
#include "ares_dns.h"
#endif

#include "rutil/DnsUtil.hxx"
#include "rutil/Inserter.hxx"
#include "rutil/Logger.hxx"
#include "rutil/ParseBuffer.hxx"
#include "rutil/Random.hxx"
#include "rutil/compat.hxx"
#include "rutil/WinLeakCheck.hxx"
#include "rutil/dns/DnsHandler.hxx"
#include "rutil/dns/QueryTypes.hxx"
#include "rutil/dns/DnsStub.hxx"
#include "rutil/dns/DnsNaptrRecord.hxx"
#include "resip/stack/DnsResult.hxx"
#include "resip/stack/DnsInterface.hxx"
#include "resip/stack/Tuple.hxx"
#include "resip/stack/Uri.hxx"
#include "rutil/WinLeakCheck.hxx"


#if !defined(USE_ARES)
#warning "ARES is required"
#endif

using namespace resip;
using namespace std;

#define RESIPROCATE_SUBSYSTEM resip::Subsystem::DNS

DnsResult::DnsResult(DnsInterface& interfaceObj, DnsStub& dns, RRVip& vip, 
DnsHandler* handler) 
   : mInterface(interfaceObj),
     mDns(dns),
     mVip(vip),
     mHandler(handler),
     mSRVCount(0),
     mDoingEnum(false),
     mSips(false),
     mTransport(UNKNOWN_TRANSPORT),
     mPort(-1),
     mType(Pending),
     mBlacklistLastReturnedResult(false),
         mNumSuffixes(0)
{
}

DnsResult::~DnsResult()
{
   //DebugLog (<< "DnsResult::~DnsResult() " << *this);
   assert(mType != Pending);
}

void 
DnsResult::transition(Type t)
{
   if ((t == Finished || t == Destroyed || t == Available) &&
       (mType == Pending))
   {
      mInterface.mActiveQueryCount--;
      assert(mInterface.mActiveQueryCount >= 0);
   }
   mType = t;
}

void
DnsResult::destroy()
{
   assert(this);
   //DebugLog (<< "DnsResult::destroy() " << *this);
   
   if (mType == Pending)
   {
      transition(Destroyed);
   }
   else
   {
      delete this;
   }
}

DnsResult::Type
DnsResult::available()
{
   assert(mType != Destroyed);
   if (mType == Available)
   {
      if (!mResults.empty())
      {
         return Available;
      }
      else
      {
         if (mBlacklistLastReturnedResult)
         {
            blacklistLastReturnedResult();
            mBlacklistLastReturnedResult = false;
         }
         primeResults();
         return available(); // recurse
      }
   }
   else
   {
      return mType;
   }
}

Tuple
DnsResult::next() 
{
   assert(available() == Available);
   Tuple next = mResults.front();
   mResults.pop_front();
   StackLog (<< "Returning next dns entry: " << next);
  
   if (mBlacklistLastReturnedResult)
   {
      blacklistLastReturnedResult();
   }
   else if (!mCurrResultPath.empty())
   {
      mBlacklistLastReturnedResult = true;
   }
   mLastReturnedResult = next;

   assert(mCurrSuccessPath.size()<=3);
   Item top;
   if (!mCurrSuccessPath.empty())
   {
      top = mCurrSuccessPath.top();
      if (top.rrType == T_A || top.rrType == T_AAAA)
      {
         mCurrSuccessPath.pop();
      }
   }
   top.domain = next.getTargetDomain();
   top.rrType = next.isV4()? T_A : T_AAAA;
   top.value = Tuple::inet_ntop(next);
   mCurrSuccessPath.push(top);
   return next;
}

void DnsResult::success()
{
   while (!mCurrSuccessPath.empty())
   {
      Item top = mCurrSuccessPath.top();
      DebugLog( << "Whitelisting " << top.domain << "(" << top.rrType << "): " 
<< top.value);
      mVip.vip(top.domain, top.rrType, top.value);
      mCurrSuccessPath.pop();
   }
}

void
DnsResult::lookup(const Uri& uri, const std::vector<Data> &enumSuffixes)
{
   DebugLog (<< "DnsResult::lookup " << uri);
   //int type = this->mType;
   mDoingEnum = false;
   mNumSuffixes = 0;
   if (!enumSuffixes.empty() && uri.isEnumSearchable())
   {
      mInputUri = uri;
      std::vector<Data> enums = uri.getEnumLookups(enumSuffixes);
      mNumSuffixes = enums.size();
      for(int nn =0; nn<mNumSuffixes; nn++)
      {
         DebugLog (<< "Doing ENUM lookup on " << enums[nn]);
         mDns.lookup<RR_NAPTR>(enums[nn], Protocol::Enum, this); 
      }
   }

   if (mNumSuffixes)
   {
         mDoingEnum = true;
   }
   else
   {
     lookupInternal(uri);
   }
}

void
DnsResult::lookupInternal(const Uri& uri)
{
   //assert(uri.scheme() == Symbols::Sips || uri.scheme() == Symbols::Sip);  
   mSips = (uri.scheme() == Symbols::Sips);
   mTarget = (!mSips && uri.exists(p_maddr)) ? uri.param(p_maddr) : uri.host();
   mSrvKey = Symbols::UNDERSCORE + uri.scheme().substr(0, uri.scheme().size()) 
+ Symbols::DOT;
   bool isNumeric = DnsUtil::isIpAddress(mTarget);

   if (uri.exists(p_transport))
   {
      mTransport = Tuple::toTransport(uri.param(p_transport));

      if (isNumeric) // IP address specified
      {
         mPort = getDefaultPort(mTransport, uri.port());
         Tuple tuple(mTarget, mPort, mTransport, mTarget);
         DebugLog (<< "Found immediate result: " << tuple);
         mResults.push_back(tuple);
         transition(Available);
         if (mHandler) mHandler->handle(this);         
      }
      else if (uri.port() != 0)
      {
         mPort = uri.port();
         lookupHost(mTarget); // for current target and port
      }
      else 
      { 
         if (mSips)
         {
            if (mTransport == UDP)
            {
               mTransport = DTLS;
               if (!mInterface.isSupportedProtocol(mTransport))
               {
                  transition(Finished);
                  if (mHandler) mHandler->handle(this);
                  return;
               }
               mSRVCount++;
               mDns.lookup<RR_SRV>("_sips._udp." + mTarget, Protocol::Sip, 
this);
               StackLog (<< "Doing SRV lookup of _sips._udp." << mTarget);
            }
            else
            {
               mTransport = TLS;
               if (!mInterface.isSupportedProtocol(mTransport))
               {
                  transition(Finished);
                  if (mHandler) mHandler->handle(this);
                  return;
               }
               mSRVCount++;
               mDns.lookup<RR_SRV>("_sips._tcp." + mTarget, Protocol::Sip,  
this);
               StackLog (<< "Doing SRV lookup of _sips._tcp." << mTarget);
            }
         }
         else
         {
            if (!mInterface.isSupportedProtocol(mTransport))
            {
               transition(Finished);
               if (mHandler) mHandler->handle(this);
               return;
            }

            switch(mTransport)
            {
               case TLS: //deprecated, mean TLS over TCP
                  mSRVCount++;
                  mDns.lookup<RR_SRV>("_sips._tcp." + mTarget, Protocol::Sip, 
this);
                  StackLog (<< "Doing SRV lookup of _sips._tcp." << mTarget);
                  break;
               case DTLS: //deprecated, mean TLS over TCP
                  mSRVCount++;
                  mDns.lookup<RR_SRV>("_sip._dtls." + mTarget, Protocol::Sip, 
this);
                  StackLog (<< "Doing SRV lookup of _sip._dtls." << mTarget);
                  break;
               case TCP:
                  mSRVCount++;
                  mDns.lookup<RR_SRV>("_sip._tcp." + mTarget, Protocol::Sip, 
this);
                  StackLog (<< "Doing SRV lookup of _sip._tcp." << mTarget);
                  break;
               case SCTP:
               case DCCP:
               case UDP:
               default: //fall through to UDP for unimplemented & unknown
                  mSRVCount++;
                  mDns.lookup<RR_SRV>("_sip._udp." + mTarget, Protocol::Sip, 
this);
                  StackLog (<< "Doing SRV lookup of _sip._udp." << mTarget);
            }
         }
      }
   }
   else 
   {
      if (isNumeric || uri.port() != 0)
      {
         if (mSips || mTransport == TLS)
         {
            mTransport = TLS;
         }
         else 
         {
            mTransport = UDP;
         }

         if (isNumeric) // IP address specified
         {
            mPort = getDefaultPort(mTransport, uri.port());
            Tuple tuple(mTarget, mPort, mTransport, mTarget);
            mResults.push_back(tuple);
            transition(Available);
            DebugLog (<< "Numeric result so return immediately: " << tuple);
            if (mHandler) mHandler->handle(this);
         }
         else // port specified so we know the transport
         {
            mPort = uri.port();
            if (mInterface.isSupported(mTransport, V6) || 
mInterface.isSupported(mTransport, V4))
            {
               lookupHost(mTarget);
            }
            else
            {
               assert(0);
               if (mHandler) mHandler->handle(this);
            }
         }
      }
      else // do NAPTR
      {
         mDns.lookup<RR_NAPTR>(mTarget, Protocol::Sip, this); // for current 
target
      }
   }
}

void DnsResult::lookupHost(const Data& target)
{
   if (mInterface.isSupported(mTransport, V6))
   {
#ifdef USE_IPV6
      DebugLog(<< "Doing host (AAAA) lookup: " << target);
      mPassHostFromAAAAtoA = target;
      mDns.lookup<RR_AAAA>(target, Protocol::Sip, this);
#else
      assert(0);
      mDns.lookup<RR_A>(target, Protocol::Sip, this);
#endif
   }
   else if (mInterface.isSupported(mTransport, V4))
   {
      mDns.lookup<RR_A>(target, Protocol::Sip, this);
   }
   else
   {
      assert(0);
   }
}

int
DnsResult::getDefaultPort(TransportType transport, int port)
{
   if (port == 0)
   {
      switch (transport)
      {
         case UDP:
            return Symbols::DefaultSipPort;
         case TCP:
            return mSips ? Symbols::DefaultSipsPort : Symbols::DefaultSipPort;
         case TLS:
         case DTLS:
            return Symbols::DefaultSipsPort;
         default:
            ErrLog( << "Should not get this - unknown transport" );
            return Symbols::DefaultSipPort; // !cj! todo - remove 
            assert(0);
      }
   }
   else
   {
      return port;
   }

        assert(0);
        return 0;
}

void
DnsResult::primeResults()
{
   StackLog(<< "Priming " << Inserter(mSRVResults));
   //assert(mType != Pending);
   //assert(mType != Finished);
   assert(mResults.empty());

   if (!mSRVResults.empty())
   {
      SRV next = retrieveSRV();
      StackLog (<< "Primed with SRV=" << next);
      transition(Pending);
      mPort = next.port;
      mTransport = next.transport;
      StackLog (<< "No A or AAAA record for " << next.target << " in additional 
records");
      if (mInterface.isSupported(mTransport, V6) || 
mInterface.isSupported(mTransport, V4))
      {
         assert(mCurrResultPath.size()<=2);
         Item top;
         if (!mCurrResultPath.empty())
         {
            top = mCurrResultPath.top();
            if (top.rrType == T_SRV)
            {
               vector<Data> records;
               records.push_back(top.value);
               mDns.blacklist(top.domain, top.rrType, Protocol::Sip, records);
               mCurrResultPath.pop();
            }
            else
            {
               assert(top.rrType==T_NAPTR);
            }
         }

         while (!mCurrSuccessPath.empty())
         {
            top = mCurrSuccessPath.top();
            if (top.rrType != T_NAPTR)
            {
               mCurrSuccessPath.pop();
            }
            else
            {
               break;
            }
         }
         top.domain = next.key;
         top.rrType = T_SRV;
         top.value = next.target + ":" + Data(next.port);
         mCurrResultPath.push(top);
         mCurrSuccessPath.push(top);
         lookupHost(next.target);
      }
      else
      {
         assert(0);
         if (mHandler) mHandler->handle(this);
      }
      // don't call primeResults since we need to wait for the response to
      // AAAA/A query first
   }
   else
   {
      bool changed = (mType == Pending);
      transition(Finished);
      if (!mCurrResultPath.empty())
      {
         assert(mCurrResultPath.size()<=2);
         while (!mCurrResultPath.empty())
         {
            Item top = mCurrResultPath.top();
            vector<Data> records;
            records.push_back(top.value);
            mDns.blacklist(top.domain, top.rrType, Protocol::Sip, records);
            mCurrResultPath.pop();
         }
      }
      if (changed && mHandler) mHandler->handle(this);
   }

   // Either we are finished or there are results primed
   //assert(mType == Finished ||        // !slg! handle() might end up 
destroying the DnsResult - so we can't safely do this assert
   //       mType == Pending || 
   //       (mType == Available && !mResults.empty())
   //   );
}

// implement the selection algorithm from rfc2782 (SRV records)
DnsResult::SRV 
DnsResult::retrieveSRV()
{
    // !ah! if mTransport is known -- should we ignore those that don't match?!
   assert(!mSRVResults.empty());

   const SRV& srv = *mSRVResults.begin();
   if (srv.cumulativeWeight == 0)
   {
      int priority = srv.priority;
   
     mCumulativeWeight=0;
      //!dcm! -- this should be fixed properly; TCP req. lines were being sent
      //out on UDP
      TransportType transport = mSRVResults.begin()->transport;      
      for (std::vector<SRV>::iterator i=mSRVResults.begin(); 
           i!=mSRVResults.end() 
              && i->priority == priority 
              && i->transport == transport; i++)
      {
         mCumulativeWeight += i->weight;
         i->cumulativeWeight = mCumulativeWeight;
      }
   }
   
   int selected = Random::getRandom() % (mCumulativeWeight+1);

   StackLog (<< "cumulative weight = " << mCumulativeWeight << " selected=" << 
selected);

   std::vector<SRV>::iterator i;
   for (i=mSRVResults.begin(); i!=mSRVResults.end(); i++)
   {
      if (i->cumulativeWeight >= selected)
      {
         break;
      }
   }
   
   if (i == mSRVResults.end())
   {
      InfoLog (<< "SRV Results problem selected=" << selected << " cum=" << 
mCumulativeWeight);
   }
   assert(i != mSRVResults.end());
   SRV next = *i;
   mCumulativeWeight -= next.cumulativeWeight;
   mSRVResults.erase(i);
   
   StackLog (<< "SRV: " << Inserter(mSRVResults));

   return next;
}

// adapted from the adig.c example in ares
const unsigned char* 
DnsResult::skipDNSQuestion(const unsigned char *aptr,
                           const unsigned char *abuf,
                           int alen)
{
   char *name=0;
   int status=0;
   int len=0;
   
   // Parse the question name. 
   status = ares_expand_name(aptr, abuf, alen, &name, &len);
   if (status != ARES_SUCCESS)
   {
      StackLog (<< "Failed parse of RR");
      return NULL;
   }
   aptr += len;

   // Make sure there's enough data after the name for the fixed part
   // of the question.
   if (aptr + QFIXEDSZ > abuf + alen)
   {
      free(name);
      StackLog (<< "Failed parse of RR");
      return NULL;
   }

   // Parse the question type and class. 
   //int type = DNS_QUESTION_TYPE(aptr);
   //int dnsclass = DNS_QUESTION_CLASS(aptr);
   aptr += QFIXEDSZ;
   
   free(name);
   return aptr;
}

DnsResult::NAPTR::NAPTR() : order(0), pref(0)
{
}

bool 
DnsResult::NAPTR::operator<(const DnsResult::NAPTR& rhs) const
{
   if (key.empty()) // default value
   {
      return false;
   }
   else if (rhs.key.empty()) // default value
   {
      return true;
   }
   else if (order < rhs.order)
   {
      return true;
   }
   else if (order == rhs.order)
   {
      if (pref < rhs.pref)
      {
         return true;
      }
      else if (pref == rhs.pref)
      {
         return replacement < rhs.replacement;
      }
   }
   return false;
}

DnsResult::SRV::SRV() : priority(0), weight(0), cumulativeWeight(0), port(0)
{
}

bool 
DnsResult::SRV::operator<(const DnsResult::SRV& rhs) const
{
   if (transport < rhs.transport)
   {
      return true;
   }
   else if (transport == rhs.transport)
   {
      if (priority < rhs.priority)
      {
         return true;
      }
      else if (priority == rhs.priority)
      {
         if (weight < rhs.weight)
         {
            return true;
         }
         else if (weight == rhs.weight)
         {
            if (target < rhs.target)
            {
               return true;
            }
         }
      }
   }
   return false;
}

void DnsResult::onDnsResult(const DNSResult<DnsHostRecord>& result)
{
   if (!mInterface.isSupported(mTransport, V4) && 
!mInterface.isSupported(mTransport, V6))
   {
      return;
   }
   StackLog (<< "Received dns result for: " << mTarget);
   StackLog (<< "DnsResult::onDnsResult() " << result.status);
   
   // This function assumes that the A query that caused this callback
   // is the _only_ outstanding DNS query that might result in a
   // callback into this function

   if ( mType == Destroyed )
   {
      destroy();
      return;
   }

   if (result.status == 0)
   {
      for (vector<DnsHostRecord>::const_iterator it = result.records.begin(); 
it != result.records.end(); ++it)
      {
         in_addr addr;
         addr.s_addr = (*it).addr().s_addr;
         Tuple tuple(addr, mPort, mTransport, mTarget);
         StackLog (<< "Adding " << tuple << " to result set");
         mResults.push_back(tuple);
      }
   }
   else
   {
      StackLog (<< "Failed async A query: " << result.msg);
   }

   if (mSRVCount == 0)
   {
      bool changed = (mType == Pending);
      if (mResults.empty())
      {
#ifdef WIN32_SYNCRONOUS_RESOLUTION_ON_ARES_FAILURE
         // Try Windows Name Resolution (not asyncronous)
         WSAQUERYSET QuerySet = { 0 };
             GUID guidServiceTypeUDP = SVCID_UDP(mPort);
             GUID guidServiceTypeTCP = SVCID_TCP(mPort);
         HANDLE hQuery;
         QuerySet.dwSize = sizeof(WSAQUERYSET);
         QuerySet.lpServiceClassId = mTransport == UDP ? &guidServiceTypeUDP : 
&guidServiceTypeTCP;
         QuerySet.dwNameSpace = NS_ALL;
         QuerySet.lpszServiceInstanceName = (char *)mTarget.c_str();
         if(WSALookupServiceBegin(&QuerySet, LUP_RETURN_ADDR, &hQuery) == 0)
         {
             DWORD dwQuerySize = 256;   // Starting size
             int iRet = 0;
             bool fDone = false;
             LPWSAQUERYSET pQueryResult = (LPWSAQUERYSET) new char[dwQuerySize];
             while(iRet == 0 && pQueryResult)
             {
                iRet = WSALookupServiceNext(hQuery, 0, &dwQuerySize, 
pQueryResult);
                if(pQueryResult && iRet == -1 && GetLastError() == WSAEFAULT)
                {
                   delete [] pQueryResult;
                   pQueryResult = (LPWSAQUERYSET) new char[dwQuerySize]; // 
Re-allocate new size
                   iRet = WSALookupServiceNext(hQuery, 0, &dwQuerySize, 
pQueryResult);
                }
                if(pQueryResult && iRet == 0)
                {
                   for(DWORD i = 0; i < pQueryResult->dwNumberOfCsAddrs; i++)
                   {
                      SOCKADDR_IN *pSockAddrIn = (SOCKADDR_IN 
*)pQueryResult->lpcsaBuffer[i].RemoteAddr.lpSockaddr;
                      Tuple tuple(pSockAddrIn->sin_addr, mPort, mTransport, 
mTarget);
                      StackLog (<< "Adding (WIN) " << tuple << " to result 
set");
                      mResults.push_back(tuple);
                      mType = Available;
                   }
                }
             }
             delete [] pQueryResult;
             WSALookupServiceEnd(hQuery);
         }
         if(mResults.empty())
         {
            mType = Finished; 
         }
#else
         mType = Finished; 
#endif
         clearCurrPath();
      }
      else 
      {
         if (mSRVResults.empty())
         {
            transition(Available);
         }
         else
         {
            mType = Available;
         }
         addToPath(mResults);
      }
      if (changed && mHandler) mHandler->handle(this);
   }
}

#ifdef USE_IPV6
void DnsResult::onDnsResult(const DNSResult<DnsAAAARecord>& result)
{
   StackLog (<< "Received AAAA result for: " << mTarget);
   if (!mInterface.isSupported(mTransport, V6))
   {
      return;
   }
   StackLog (<< "DnsResult::onDnsResult() " << result.status);
   assert(mInterface.isSupported(mTransport, V6));

   // This function assumes that the AAAA query that caused this callback
   // is the _only_ outstanding DNS query that might result in a
   // callback into this function
   if ( mType == Destroyed )
   {
      destroy();
      return;
   }

   if (result.status == 0)
   {
      for (vector<DnsAAAARecord>::const_iterator it = result.records.begin(); 
it != result.records.end(); ++it)
      {
         Tuple tuple((*it).v6Address(), mPort, mTransport, mTarget);
         StackLog (<< "Adding " << tuple << " to result set");
         mResults.push_back(tuple);
      }
   }
   else
   {
      StackLog (<< "Failed async AAAA query: " << result.msg);
   }
   // funnel through to host processing
   mDns.lookup<RR_A>(mPassHostFromAAAAtoA, Protocol::Sip, this);

}
#endif

void DnsResult::onDnsResult(const DNSResult<DnsSrvRecord>& result)
{
   StackLog (<< "Received SRV result for: " << mTarget);
   assert(mSRVCount>=0);
   mSRVCount--;
   StackLog (<< "DnsResult::onDnsResult() " << mSRVCount << " status=" << 
result.status);

   // There could be multiple SRV queries outstanding, but there will be no
   // other DNS queries outstanding that might cause a callback into this
   // object.
   if (mType == Destroyed && mSRVCount == 0)
   {
      destroy();
      return;
   }

   if (result.status == 0)
   {
      for (vector<DnsSrvRecord>::const_iterator it = result.records.begin(); it 
!= result.records.end(); ++it)
      {
         SRV srv;
         srv.key = (*it).name();
         srv.priority = (*it).priority();
         srv.weight = (*it).weight();
         srv.port = (*it).port();
         srv.target = (*it).target();
         if (srv.key.find("_sips._udp") != Data::npos)
         {
            srv.transport = DTLS;
         }
         else if (srv.key.find("_sips._tcp") != Data::npos)
         {
            srv.transport = TLS;
         }
         else if (srv.key.find("_udp") != Data::npos)
         {
            srv.transport = UDP;
         }
         else if (srv.key.find("_dtls") != Data::npos)
         {
            srv.transport = DTLS;
         }
         else if (srv.key.find("_tls") != Data::npos)
         {
            srv.transport = TLS;
         }
         else if (srv.key.find("_tcp") != Data::npos)
         {
            srv.transport = TCP;
         }
         else
         {
            StackLog (<< "Skipping SRV " << srv.key);
            continue;
         }
         mSRVResults.push_back(srv);
      }
   }
   else
   {
      StackLog (<< "SRV lookup failed: " << result.domain << " " << 
result.status);
   }

   // no outstanding queries 
   if (mSRVCount == 0) 
   {
      if (mSRVResults.empty())
      {
         if (mTransport == UNKNOWN_TRANSPORT)
         {
            if (mSips)
            {
               mTransport = TLS;
               mPort = Symbols::DefaultSipsPort;
            }
            else
            {
               mTransport = UDP;
               mPort = Symbols::DefaultSipPort;
            }
         }
         else
         {
            mPort = getDefaultPort(mTransport, 0);
         }
         
         StackLog (<< "No SRV records for " << mTarget << ". Trying A records");
         if (mInterface.isSupported(mTransport, V6) || 
mInterface.isSupported(mTransport, V4))
         {
            lookupHost(mTarget);
         }
         else
         {
            primeResults();
         }
      }
      else
      {
         std::sort(mSRVResults.begin(),mSRVResults.end()); // !jf! uggh
         primeResults();
      }
   }
}

void
DnsResult::onEnumResult(const DNSResult<DnsNaptrRecord>& result)
{
   if (result.status == 0)
   {
      static Data enumService1("e2u+sip");
      static Data enumService2("sip+e2u");

      DnsNaptrRecord best;
      best.order() = -1;

      for (vector<DnsNaptrRecord>::const_iterator i = result.records.begin(); i 
!= result.records.end(); ++i)
      {
         InfoLog (<< "service=" << i->service()
                  << " order=" << i->order()
                  << " flags="  << i->flags() 
                  << " regexp substitution=" << i->regexp().replacement()
                  << " replacement=" << i->replacement());

         if ( (isEqualNoCase(i->service(), enumService1) ||
               isEqualNoCase(i->service(), enumService2) )  && // only E2U 
records
              //i->flags().find("u") != Data::npos && // must be terminal record
              i->replacement().empty() )
               
         {
            if (best.order() == -1)
            {
               best = *i;
            }
            else if (i->order() < best.order())
            {
               best = *i;
            }
            else if (i->order() == best.order() && 
                     i->preference() < best.preference())
            {
               best = *i;
            }
         }
      }
      
      if (best.order() != -1)
      {
                 mNumSuffixes = 0;
                 mDoingEnum = false;
         InfoLog (<< "Found an enum result: " << best.regexp().replacement());
         try
         {
            Uri rewrite(best.regexp().apply(Data::from(mInputUri)));
            InfoLog (<< "Rewrote uri " << mInputUri << " -> " << rewrite);
            mHandler->rewriteRequest(rewrite);
            lookupInternal(rewrite);
         }
         catch (ParseBuffer::Exception& /* e */)
         {
            lookupInternal(mInputUri);
         }
      }
      else
      {
         lookupInternal(mInputUri);
      }
   }
   else if (mNumSuffixes == 0)
   {
      lookupInternal(mInputUri);
   }
}

void
DnsResult::onNaptrResult(const DNSResult<DnsNaptrRecord>& result)
{
   bool bFail = false;
   if (result.status == 0)
   {
      for (vector<DnsNaptrRecord>::const_iterator it = result.records.begin(); 
it != result.records.end(); ++it)
      {
         NAPTR naptr;
         naptr.key = (*it).name();
         naptr.flags = (*it).flags();
         naptr.order = (*it).order();
         naptr.pref = (*it).preference();
         naptr.regex = (*it).regexp();
         naptr.replacement = (*it).replacement();
         naptr.service = (*it).service();
         
         StackLog (<< "Adding NAPTR record: " << naptr);
         
         if ( mSips && naptr.service.find("SIPS") == 0)
         {
            if (mInterface.isSupported(naptr.service) && naptr < 
mPreferredNAPTR)
            {
               mPreferredNAPTR = naptr;
               StackLog (<< "Picked preferred: " << mPreferredNAPTR);
            }
         }
         else if (mInterface.isSupported(naptr.service) && naptr < 
mPreferredNAPTR)
         {
            mPreferredNAPTR = naptr;
            StackLog (<< "Picked preferred: " << mPreferredNAPTR);
         }
      }

      // This means that dns / NAPTR is misconfigured for this client 
      if (mPreferredNAPTR.key.empty())
      {
         StackLog (<< "There are no NAPTR records supported by this client so 
do an SRV lookup instead");
         bFail = true;
      }
      else
      {
         transition(Pending);
         Item item;
         item.domain = mPreferredNAPTR.key;
         item.rrType = T_NAPTR;
         item.value = mPreferredNAPTR.replacement;
         clearCurrPath();
         mCurrResultPath.push(item);
         mCurrSuccessPath.push(item);
         mSRVCount++;
         InfoLog (<< "Doing SRV lookup of " << mPreferredNAPTR.replacement);
         mDns.lookup<RR_SRV>(mPreferredNAPTR.replacement, Protocol::Sip, this);
      }
   }
   else
   {
      if (result.status > 6)
      {
         DebugLog (<< "NAPTR lookup failed: " << result.domain << " " << 
result.msg);
      }
      else
      {
         StackLog (<< "NAPTR lookup failed: " << result.domain << " " << 
result.msg);
      }
      bFail = true;
   }

   if (bFail)
   {

      if (mSips)
      {
         if (!mInterface.isSupportedProtocol(TLS))
         {
            transition(Finished);
            if (mHandler) mHandler->handle(this);
            return;
         }

         mSRVCount++;
         mDns.lookup<RR_SRV>("_sips._tcp." + mTarget, Protocol::Sip, this);
         StackLog (<< "Doing SRV lookup of _sips._tcp." << mTarget);
      }
      else
      {
         if (mInterface.isSupportedProtocol(TLS))
         {
            mDns.lookup<RR_SRV>("_sips._tcp." + mTarget, Protocol::Sip, this);
            ++mSRVCount;
            StackLog (<< "Doing SRV lookup of _sips._tcp." << mTarget);
         }
         if (mInterface.isSupportedProtocol(DTLS))
         {
            mDns.lookup<RR_SRV>("_sips._udp." + mTarget, Protocol::Sip, this);
            ++mSRVCount;
            StackLog (<< "Doing SRV lookup of _sips._udp." << mTarget);
         }
         if (mInterface.isSupportedProtocol(TCP))
         {
            mDns.lookup<RR_SRV>("_sip._tcp." + mTarget, Protocol::Sip, this);
            ++mSRVCount;
            StackLog (<< "Doing SRV lookup of _sip._tcp." << mTarget);
         }
         if (mInterface.isSupportedProtocol(UDP))
         {
            mDns.lookup<RR_SRV>("_sip._udp." + mTarget, Protocol::Sip, this);
            ++mSRVCount;
            StackLog (<< "Doing SRV lookup of _sip._udp." << mTarget);
         }
      }
   }
}

void 
DnsResult::onDnsResult(const DNSResult<DnsNaptrRecord>& result)
{
   StackLog (<< "Received NAPTR result for: " << mInputUri << " target=" << 
mTarget);
   StackLog (<< "DnsResult::onDnsResult() " << result.status << " mNumSuffixes 
"<<(int) mNumSuffixes <<" mDoingEnum " <<mDoingEnum);

   // This function assumes that the NAPTR query that caused this
   // callback is the ONLY outstanding query that might cause
   // a callback into this object

   if (mType == Destroyed)
   {
      destroy();
      return;
   }

   if (mNumSuffixes)
   {
      --mNumSuffixes;
      if (mDoingEnum)
      {
         onEnumResult(result);
      }
      else
      {
         onNaptrResult(result);
      }
  }
  else
  {
     /************************************************
                   Ignore
        *************************************************/
        mDoingEnum = false;
  }
  
}

void DnsResult::onDnsResult(const DNSResult<DnsCnameRecord>& result)
{
}

void DnsResult::clearCurrPath()
{
   while (!mCurrResultPath.empty())
   {
      mCurrResultPath.pop();
   }

   while (!mCurrSuccessPath.empty())
   {
      mCurrSuccessPath.pop();
   }
}

void DnsResult::blacklistLastReturnedResult()
{
   assert(!mCurrResultPath.empty());
   Item top = mCurrResultPath.top();
   assert(top.rrType==T_A || top.rrType==T_AAAA);
   assert(top.domain==mLastReturnedResult.getTargetDomain());
   assert(top.value==Tuple::inet_ntop(mLastReturnedResult));
   vector<Data> records;
   records.push_back(top.value);
   DebugLog( << "Blacklisting " << top.domain << "(" << top.rrType << "): " << 
top.value);
   mDns.blacklist(top.domain, top.rrType, Protocol::Sip, records);
   DebugLog( << "Remove vip" << top.domain << "(" << top.rrType << ")");
   mVip.removeVip(top.domain, top.rrType);
   mCurrResultPath.pop();
}

void DnsResult::addToPath(const std::deque<Tuple>& results)
{
   assert(mCurrResultPath.size()<=2);
   for (std::deque<Tuple>::const_reverse_iterator it = results.rbegin(); it != 
results.rend(); ++it)
   {
      Item item;
      item.domain = (*it).getTargetDomain();
      item.rrType = (*it).isV4()? T_A : T_AAAA;
      item.value = Tuple::inet_ntop((*it));
      mCurrResultPath.push(item);
   }
}

std::ostream& 
resip::operator<<(std::ostream& strm, const resip::DnsResult& result)
{
   strm << result.mTarget << " --> " << Inserter(result.mResults);
   return strm;
}


std::ostream& 
resip::operator<<(std::ostream& strm, const resip::DnsResult::NAPTR& naptr)
{
   strm << "key=" << naptr.key
        << " order=" << naptr.order
        << " pref=" << naptr.pref
        << " flags=" << naptr.flags
        << " service=" << naptr.service
        << " regex=" << naptr.regex.regexp() << " -> " << 
naptr.regex.replacement()
        << " replacement=" << naptr.replacement;
   return strm;
}

std::ostream& 
resip::operator<<(std::ostream& strm, const resip::DnsResult::SRV& srv)
{
   strm << "key=" << srv.key
        << " t=" << Tuple::toData(srv.transport) 
        << " p=" << srv.priority
        << " w=" << srv.weight
        << " c=" << srv.cumulativeWeight
        << " port=" << srv.port
        << " target=" << srv.target;
   return strm;
}


//  Copyright (c) 2003, Jason Fischl 
/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 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@xxxxxxxxxxx
 *
 * 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/>.
 *
 */