reSIProcate/stack  9694
DnsResult.cxx
Go to the documentation of this file.
00001 #if defined(HAVE_CONFIG_H)
00002 #include "config.h"
00003 #endif
00004 
00005 #include <algorithm>
00006 #include <stack>
00007 
00008 #ifndef WIN32
00009 #include <sys/types.h>
00010 #include <sys/socket.h>
00011 #include <arpa/inet.h>
00012 #ifndef __CYGWIN__
00013 #  include <netinet/in.h>
00014 #  include <arpa/nameser.h>
00015 #  include <resolv.h>
00016 #endif
00017 #include <netdb.h>
00018 #include <netinet/in.h>
00019 #else
00020 #include <Winsock2.h>
00021 #include <svcguid.h>
00022 #ifdef USE_IPV6
00023 #include <ws2tcpip.h>
00024 #endif
00025 #endif
00026 
00027 #include "rutil/DnsUtil.hxx"
00028 #include "rutil/Inserter.hxx"
00029 #include "rutil/Logger.hxx"
00030 #include "rutil/ParseBuffer.hxx"
00031 #include "rutil/Random.hxx"
00032 #include "rutil/compat.hxx"
00033 #include "rutil/Timer.hxx"
00034 #include "rutil/dns/DnsHandler.hxx"
00035 #include "rutil/dns/QueryTypes.hxx"
00036 #include "rutil/dns/DnsStub.hxx"
00037 #include "rutil/dns/DnsNaptrRecord.hxx"
00038 #include "resip/stack/DnsResult.hxx"
00039 #include "resip/stack/DnsInterface.hxx"
00040 #include "resip/stack/TupleMarkManager.hxx"
00041 #include "resip/stack/Tuple.hxx"
00042 #include "resip/stack/Uri.hxx"
00043 #include "rutil/WinLeakCheck.hxx"  // not compatible with placement new used below
00044 
00045 using namespace resip;
00046 using namespace std;
00047 
00048 #define RESIPROCATE_SUBSYSTEM resip::Subsystem::DNS
00049 
00050 DnsResult::DnsResult(DnsInterface& interfaceObj, DnsStub& dns, RRVip& vip, DnsHandler* handler) 
00051    : mInterface(interfaceObj),
00052      mDns(dns),
00053      mVip(vip),
00054      mHandler(handler),
00055      mSRVCount(0),
00056      mDoingEnum(false),
00057      mSips(false),
00058      mTransport(UNKNOWN_TRANSPORT),
00059      mPort(-1),
00060      mHaveChosenTransport(false),
00061      mType(Pending),
00062      mCumulativeWeight(0),
00063      mHaveReturnedResults(false)
00064 {
00065 }
00066 
00067 DnsResult::~DnsResult()
00068 {
00069    //DebugLog (<< "DnsResult::~DnsResult() " << *this);
00070    assert(mType != Pending);
00071 }
00072 
00073 void 
00074 DnsResult::transition(Type t)
00075 {
00076    if((t == Pending || t== Available) && 
00077          (mType== Finished || mType == Destroyed) )
00078    {
00079       assert(0);
00080    }
00081    
00082    mType = t;
00083 }
00084 
00085 void
00086 DnsResult::destroy()
00087 {
00088    assert(this);
00089    //DebugLog (<< "DnsResult::destroy() " << *this);
00090    
00091    if (mType == Pending)
00092    {
00093       transition(Destroyed);
00094    }
00095    else
00096    {
00097       transition(Finished);
00098       delete this;
00099    }
00100 }
00101 
00102 bool
00103 DnsResult::blacklistLast(UInt64 expiry)
00104 {
00105    if(mHaveReturnedResults)
00106    {
00107       assert(!mLastReturnedPath.empty());
00108       assert(mLastReturnedPath.size()<=3);
00109       Item top = mLastReturnedPath.back();
00110    
00111       mInterface.getMarkManager().mark(mLastResult,expiry,TupleMarkManager::BLACK);
00112    
00113       DebugLog( << "Remove vip " << top.domain << "(" << top.rrType << ")");
00114       mVip.removeVip(top.domain, top.rrType);
00115       return true;
00116    }
00117    
00118    return false;
00119 }
00120 
00121 bool
00122 DnsResult::greylistLast(UInt64 expiry)
00123 {
00124    if(mHaveReturnedResults)
00125    {
00126       assert(!mLastReturnedPath.empty());
00127       assert(mLastReturnedPath.size()<=3);
00128       Item top = mLastReturnedPath.back();
00129    
00130       mInterface.getMarkManager().mark(mLastResult,expiry,TupleMarkManager::GREY);
00131    
00132       DebugLog( << "Remove vip " << top.domain << "(" << top.rrType << ")");
00133       mVip.removeVip(top.domain, top.rrType);
00134       return true;
00135    }
00136    
00137    return false;
00138 }
00139 
00140 DnsResult::Type
00141 DnsResult::available()
00142 {
00143    assert(mType != Destroyed);
00144    if (mType == Available)
00145    {
00146       if (!mResults.empty())
00147       {
00148          return Available;
00149       }
00150       else
00151       {
00152          primeResults();
00153          return available(); // recurse
00154       }
00155    }
00156    else
00157    {
00158       return mType;
00159    }
00160 }
00161 
00162 Tuple
00163 DnsResult::next()
00164 {
00165    assert(available()==Available);
00166    assert(mCurrentPath.size()<=3);
00167    
00168    mLastResult=mResults.front();
00169    mResults.pop_front();
00170    
00171    if(!mCurrentPath.empty() && 
00172       (mCurrentPath.back().rrType==T_A || mCurrentPath.back().rrType==T_AAAA))
00173    {
00174       mCurrentPath.pop_back();
00175    }
00176    
00177    Item AorAAAA;
00178    AorAAAA.domain = mLastResult.getTargetDomain();
00179    AorAAAA.rrType = mLastResult.isV4() ? T_A : T_AAAA;
00180    AorAAAA.value = Tuple::inet_ntop(mLastResult);
00181    mCurrentPath.push_back(AorAAAA);
00182    
00183    StackLog (<< "Returning next dns entry: " << mLastResult);
00184    mLastReturnedPath=mCurrentPath;
00185    mHaveReturnedResults=true;
00186    return mLastResult;
00187 }
00188 
00189 void
00190 DnsResult::whitelistLast()
00191 {
00192    std::vector<Item>::iterator i;
00193    for (i=mLastReturnedPath.begin(); i!=mLastReturnedPath.end(); ++i)
00194    {
00195       DebugLog( << "Whitelisting " << i->domain << "(" << i->rrType << "): " << i->value);
00196       mVip.vip(i->domain, i->rrType, i->value);
00197    }
00198 }
00199 
00200 void
00201 DnsResult::lookup(const Uri& uri, const std::vector<Data> &enumSuffixes)
00202 {
00203    DebugLog (<< "DnsResult::lookup " << uri);
00204    //int type = this->mType;
00205    if (!enumSuffixes.empty() && uri.isEnumSearchable())
00206    {
00207       mInputUri = uri;
00208       mDoingEnum = true;
00209       std::vector<Data> enums = uri.getEnumLookups(enumSuffixes);
00210       assert(enums.size() <= 1);
00211       if (!enums.empty())
00212       {
00213          InfoLog (<< "Doing ENUM lookup on " << *enums.begin());
00214          mDns.lookup<RR_NAPTR>(*enums.begin(), Protocol::Enum, this); 
00215          return;
00216       }
00217    }
00218 
00219    mDoingEnum = false;
00220    lookupInternal(uri);
00221 }
00222 
00223 void
00224 DnsResult::lookupInternal(const Uri& uri)
00225 {
00226    //assert(uri.scheme() == Symbols::Sips || uri.scheme() == Symbols::Sip);  
00227    mSips = (uri.scheme() == Symbols::Sips);
00228    mTarget = (!mSips && uri.exists(p_maddr)) ? uri.param(p_maddr) : uri.host();
00229    mSrvKey = Symbols::UNDERSCORE + uri.scheme().substr(0, uri.scheme().size()) + Symbols::DOT;
00230    bool isNumeric = DnsUtil::isIpAddress(mTarget);
00231 
00232    if (uri.exists(p_transport))
00233    {
00234       mTransport = Tuple::toTransport(uri.param(p_transport));
00235       mHaveChosenTransport=true;
00236       
00237       if (isNumeric) // IP address specified
00238       {
00239          mPort = getDefaultPort(mTransport, uri.port());
00240          Tuple tuple(mTarget, mPort, mTransport, mTarget);
00241 
00242          // ?bwc? If this is greylisted, what can we do? This is the only result
00243          if(!(mInterface.getMarkManager().getMarkType(tuple)==TupleMarkManager::BLACK))
00244          {
00245             DebugLog (<< "Found immediate result: " << tuple);
00246             mResults.push_back(tuple);
00247             transition(Available);
00248             if (mHandler) mHandler->handle(this);
00249          }
00250          else
00251          {
00252             transition(Available);
00253             if (mHandler) mHandler->handle(this);
00254          }
00255 
00256       }
00257       else if (uri.port() != 0)
00258       {
00259          mPort = uri.port();
00260          lookupHost(mTarget); // for current target and port
00261       }
00262       else 
00263       { 
00264          if (mSips)
00265          {
00266             if (mTransport == UDP)
00267             {
00268                mTransport = DTLS;
00269                if (!mInterface.isSupportedProtocol(mTransport))
00270                {
00271                   transition(Finished);
00272                   if (mHandler) mHandler->handle(this);
00273                   return;
00274                }
00275                if(!mDns.supportedType(T_SRV)) 
00276                {
00277                   mPort = getDefaultPort(mTransport, uri.port());
00278                   lookupHost(mTarget); // for current target and port
00279                }
00280                else
00281                {
00282                   mSRVCount++;
00283                   mDns.lookup<RR_SRV>("_sips._udp." + mTarget, Protocol::Sip, this);
00284                   StackLog (<< "Doing SRV lookup of _sips._udp." << mTarget);
00285                }
00286             }
00287             else
00288             {
00289                mTransport = TLS;
00290                mHaveChosenTransport=true;
00291                if (!mInterface.isSupportedProtocol(mTransport))
00292                {
00293                   transition(Finished);
00294                   if (mHandler) mHandler->handle(this);
00295                   return;
00296                }
00297                if(!mDns.supportedType(T_SRV)) 
00298                {
00299                   mPort = getDefaultPort(mTransport, uri.port());
00300                   lookupHost(mTarget); // for current target and port
00301                }
00302                else
00303                {
00304                   mSRVCount++;
00305                   mDns.lookup<RR_SRV>("_sips._tcp." + mTarget, Protocol::Sip,  this);
00306                   StackLog (<< "Doing SRV lookup of _sips._tcp." << mTarget);
00307                }
00308             }
00309          }
00310          else
00311          {
00312             if (!mInterface.isSupportedProtocol(mTransport))
00313             {
00314                transition(Finished);
00315                if (mHandler) mHandler->handle(this);
00316                return;
00317             }
00318 
00319             if(!mDns.supportedType(T_SRV)) 
00320             {
00321                mPort = getDefaultPort(mTransport, uri.port());
00322                lookupHost(mTarget); // for current target and port
00323                return;
00324             }
00325 
00326             switch(mTransport)
00327             {
00328                case TLS: //deprecated, mean TLS over TCP
00329                   mSRVCount++;
00330                   mDns.lookup<RR_SRV>("_sips._tcp." + mTarget, Protocol::Sip, this);
00331                   StackLog (<< "Doing SRV lookup of _sips._tcp." << mTarget);
00332                   break;
00333                case DTLS: //deprecated, mean TLS over TCP
00334                   mSRVCount++;
00335                   mDns.lookup<RR_SRV>("_sip._dtls." + mTarget, Protocol::Sip, this);
00336                   StackLog (<< "Doing SRV lookup of _sip._dtls." << mTarget);
00337                   break;
00338                case TCP:
00339                   mSRVCount++;
00340                   mDns.lookup<RR_SRV>("_sip._tcp." + mTarget, Protocol::Sip, this);
00341                   StackLog (<< "Doing SRV lookup of _sip._tcp." << mTarget);
00342                   break;
00343                case SCTP:
00344                case DCCP:
00345                case UDP:
00346                default: //fall through to UDP for unimplemented & unknown
00347                   mSRVCount++;
00348                   mDns.lookup<RR_SRV>("_sip._udp." + mTarget, Protocol::Sip, this);
00349                   StackLog (<< "Doing SRV lookup of _sip._udp." << mTarget);
00350             }
00351          }
00352       }
00353    }
00354    else // transport parameter is not specified
00355    {
00356       // if hostname is numeric, a port is specified, or NAPTR queries are not support by the DNS layer - skip NAPTR lookup
00357       if (isNumeric || uri.port() != 0 || !mDns.supportedType(T_NAPTR))
00358       {
00359          TupleMarkManager::MarkType mark=TupleMarkManager::BLACK;
00360          Tuple tuple;
00361          
00362          if(isNumeric)
00363          {
00364             if(mSips)
00365             {
00366                if((mInterface.isSupported(TLS, V4) ||
00367                    mInterface.isSupported(TLS, V6)))
00368                {
00369                   mTransport=TLS;
00370                   mPort = getDefaultPort(mTransport,uri.port());
00371                   tuple=Tuple(mTarget,mPort,mTransport,mTarget);
00372                   mark=mInterface.getMarkManager().getMarkType(tuple);
00373                }
00374             }
00375             else
00376             {
00377                if(mark!=TupleMarkManager::OK && (mInterface.isSupported(UDP, V4) ||
00378                                     mInterface.isSupported(UDP, V6)))
00379                {
00380                   mTransport=UDP;
00381                   mPort = getDefaultPort(mTransport,uri.port());
00382                   tuple=Tuple(mTarget,mPort,mTransport,mTarget);
00383                   mark=mInterface.getMarkManager().getMarkType(tuple);
00384                }
00385                
00386                if(mark!=TupleMarkManager::OK && (mInterface.isSupported(TCP, V4) ||
00387                                     mInterface.isSupported(TCP, V6)))
00388                {
00389                   mTransport=TCP;
00390                   mPort = getDefaultPort(mTransport,uri.port());
00391                   tuple=Tuple(mTarget,mPort,mTransport,mTarget);
00392                   mark=mInterface.getMarkManager().getMarkType(tuple);
00393                }
00394                
00395                if(mark!=TupleMarkManager::OK && (mInterface.isSupported(TLS, V4) ||
00396                                     mInterface.isSupported(TLS, V6)))
00397                {
00398                   mTransport=TLS;
00399                   mPort = getDefaultPort(mTransport,uri.port());
00400                   tuple=Tuple(mTarget,mPort,mTransport,mTarget);
00401                   mark=mInterface.getMarkManager().getMarkType(tuple);
00402                }
00403             }
00404             
00405             if(mark==TupleMarkManager::OK || mark==TupleMarkManager::GREY)
00406             {
00407                mHaveChosenTransport=true;
00408                mResults.push_back(tuple);
00409                transition(Available);
00410                DebugLog (<< "Numeric result so return immediately: " << tuple);
00411             }
00412             else
00413             {
00414                // .bwc. Numeric result is blacklisted. Oh well.
00415                assert(mResults.empty());
00416                transition(Available);
00417                DebugLog(<< "Numeric result, but this result is currently blacklisted: " << tuple);
00418             }
00419             
00420             if (mHandler) mHandler->handle(this);
00421 
00422          }
00423          else // host is not numeric, so we need to make a query
00424          {
00425             mTransport=UNKNOWN_TRANSPORT;
00426             
00427             if(mSips)
00428             {
00429                if(mInterface.isSupported(TLS, V4) || mInterface.isSupported(TLS, V6))
00430                {
00431                   mTransport=TLS;
00432                }
00433             }
00434             else if(mInterface.isSupported(UDP, V4) || mInterface.isSupported(UDP, V6))
00435             {
00436                mTransport=UDP;
00437             }
00438             else if(mInterface.isSupported(TCP, V4) || mInterface.isSupported(TCP, V6))
00439             {
00440                mTransport=TCP;
00441             }
00442             else if(mInterface.isSupported(TLS, V4) || mInterface.isSupported(TLS, V6))
00443             {
00444                mTransport=TLS;
00445             }
00446             
00447             if(mTransport!=UNKNOWN_TRANSPORT)
00448             {
00449                mPort=getDefaultPort(mTransport,uri.port());
00450                lookupHost(mTarget);
00451             }
00452             else
00453             {
00454                // !bwc! Debatable.
00455                assert(0);
00456                if (mHandler) mHandler->handle(this);
00457             }
00458          }
00459       }
00460       else // do NAPTR
00461       {
00462          mDns.lookup<RR_NAPTR>(mTarget, Protocol::Sip, this); // for current target
00463       }
00464    }
00465 }
00466 
00467 void DnsResult::lookupHost(const Data& target)
00468 {
00469    if (mInterface.isSupported(mTransport, V6))
00470    {
00471 #ifdef USE_IPV6
00472       DebugLog(<< "Doing host (AAAA) lookup: " << target);
00473       mPassHostFromAAAAtoA = target;
00474       mDns.lookup<RR_AAAA>(target, Protocol::Sip, this);
00475 #else
00476       assert(0);
00477       mDns.lookup<RR_A>(target, Protocol::Sip, this);
00478 #endif
00479    }
00480    else if (mInterface.isSupported(mTransport, V4))
00481    {
00482       mDns.lookup<RR_A>(target, Protocol::Sip, this);
00483    }
00484    else
00485    {
00486       CritLog(<<"Cannot lookup target="<<target
00487               <<" because DnsInterface doesn't support transport="<<mTransport);
00488       assert(0);
00489    }
00490 }
00491 
00492 int
00493 DnsResult::getDefaultPort(TransportType transport, int port)
00494 {
00495    if (port == 0)
00496    {
00497       switch (transport)
00498       {
00499          case UDP:
00500             return Symbols::DefaultSipPort;
00501          case TCP:
00502             return mSips ? Symbols::DefaultSipsPort : Symbols::DefaultSipPort;
00503          case TLS:
00504          case DTLS:
00505             return Symbols::DefaultSipsPort;
00506          default:
00507             ErrLog( << "Should not get this - unknown transport" );
00508             return Symbols::DefaultSipPort; // !cj! todo - remove 
00509             assert(0);
00510       }
00511    }
00512    else
00513    {
00514       return port;
00515    }
00516 
00517    assert(0);
00518    return 0;
00519 }
00520 
00521 void
00522 DnsResult::primeResults()
00523 {
00524    StackLog(<< "Priming " << Inserter(mSRVResults));
00525    //assert(mType != Pending);
00526    //assert(mType != Finished);
00527    assert(mResults.empty());
00528 
00529    if (!mSRVResults.empty())
00530    {
00531       SRV next = retrieveSRV();
00532       StackLog (<< "Primed with SRV=" << next);
00533       transition(Pending);
00534       mPort = next.port;
00535       mTransport = next.transport;
00536       StackLog (<< "No A or AAAA record for " << next.target << " in additional records");
00537       if (mInterface.isSupported(mTransport, V6) || mInterface.isSupported(mTransport, V4))
00538       {
00539          Item item;
00540          clearCurrPath();
00541          // Check if SRV came from a NAPTR look up
00542          std::map<Data, NAPTR>::iterator it = mTopOrderedNAPTRs.find(next.key);
00543          if(it != mTopOrderedNAPTRs.end())
00544          {
00545             item.domain = (*it).second.key;
00546             item.rrType = T_NAPTR;
00547             item.value = (*it).second.replacement;
00548             mCurrentPath.push_back(item);
00549          }
00550          item.domain = next.key;
00551          item.rrType = T_SRV;
00552          item.value = next.target + ":" + Data(next.port);
00553          mCurrentPath.push_back(item);
00554          lookupHost(next.target);
00555       }
00556       else
00557       {
00558          assert(0);
00559          if (mHandler) mHandler->handle(this);
00560       }
00561       // don't call primeResults since we need to wait for the response to
00562       // AAAA/A query first
00563    }
00564    else if(!mGreylistedTuples.empty())
00565    {
00566       for(std::vector<Tuple>::iterator i = mGreylistedTuples.begin(); i != mGreylistedTuples.end(); ++i)
00567       {
00568          mResults.push_back(*i);
00569       }
00570       mGreylistedTuples.clear();
00571       transition(Available);
00572    }
00573    else
00574    {
00575       bool changed = (mType == Pending);
00576       transition(Finished);
00577       if (changed && mHandler) mHandler->handle(this);
00578    }
00579 
00580    // Either we are finished or there are results primed
00581 }
00582 
00583 // implement the selection algorithm from rfc2782 (SRV records)
00584 DnsResult::SRV 
00585 DnsResult::retrieveSRV()
00586 {
00587     // !ah! if mTransport is known -- should we ignore those that don't match?!
00588    assert(!mSRVResults.empty());
00589    assert(mSRVCount==0);
00590 
00591    const SRV& srv = *mSRVResults.begin();
00592    int priority = srv.priority;
00593    TransportType transport=UNKNOWN_TRANSPORT;
00594    
00595    if(!mHaveChosenTransport)
00596    {
00597       // .bwc. We have not chosen a transport yet; this happens when we fail
00598       // to find a NAPTR record, and the transport is not specified in the uri.
00599       // In this contingency, we manufacture best-guess SRV queries for each
00600       // transport we support, and try one transport at a time. This
00601       // means we might try more than one transport for the uri in question.
00602       transport = srv.transport;
00603    }
00604    else
00605    {
00606       // .bwc. We chose our transport before we started looking up SRVs.
00607       // All SRVs must match. 
00608       
00609       transport=mTransport;
00610       assert(mSRVResults.begin()->transport==transport);
00611    }
00612    
00613    if (mCumulativeWeight == 0)
00614    {
00615       for (std::vector<SRV>::iterator i=mSRVResults.begin(); 
00616            i!=mSRVResults.end() 
00617               && i->priority == priority 
00618               && i->transport == transport; i++)
00619       {
00620          assert(i->weight>=0);
00621          mCumulativeWeight += i->weight;
00622       }
00623    }
00624    
00625    int selected =0;
00626    if(mCumulativeWeight!=0)
00627    {
00628       selected = Random::getRandom() % (mCumulativeWeight);
00629    }
00630    else
00631    {
00632       // .bwc. All of the remaining SRVs (at this priority/type) have a weight
00633       // of 0. The best we can do here is pick arbitrarily. In this case, we 
00634       // will end up picking the first.
00635       // (selected will be less than the weight of the first SRV, causing the
00636       // loop below to break on the first iteration)
00637       selected=-1;
00638    }
00639    
00640    StackLog (<< "cumulative weight = " << mCumulativeWeight << " selected=" << selected);
00641 
00642    std::vector<SRV>::iterator i;
00643    int cumulativeWeight=0;
00644    for (i=mSRVResults.begin(); i!=mSRVResults.end(); ++i)
00645    {
00646       cumulativeWeight+=i->weight;
00647       if (cumulativeWeight > selected)
00648       {
00649          break;
00650       }
00651    }
00652    
00653    if (i == mSRVResults.end())
00654    {
00655       InfoLog (<< "SRV Results problem selected=" << selected << " cum=" << mCumulativeWeight);
00656    }
00657    assert(i != mSRVResults.end());
00658    SRV next = *i;
00659    mCumulativeWeight -= next.weight;
00660    mSRVResults.erase(i);
00661    
00662    if(!mSRVResults.empty())
00663    {
00664       int nextPriority=mSRVResults.begin()->priority;
00665       TransportType nextTransport=mSRVResults.begin()->transport;
00666       
00667       // .bwc. If we have finished traversing a priority value/transport type,
00668       // we reset the cumulative weight to 0, to prompt its recalculation.
00669       if(priority!=nextPriority || transport!=nextTransport)
00670       {
00671          mCumulativeWeight=0;
00672       }
00673    }
00674    
00675    StackLog (<< "SRV: " << Inserter(mSRVResults));
00676 
00677    return next;
00678 }
00679 
00680 DnsResult::NAPTR::NAPTR() : order(0), pref(0)
00681 {
00682 }
00683 
00684 bool 
00685 DnsResult::NAPTR::operator<(const DnsResult::NAPTR& rhs) const
00686 {
00687    if (key.empty()) // default value
00688    {
00689       return false;
00690    }
00691    else if (rhs.key.empty()) // default value
00692    {
00693       return true;
00694    }
00695    else if (order < rhs.order)
00696    {
00697       return true;
00698    }
00699    else if (order == rhs.order)
00700    {
00701       if (pref < rhs.pref)
00702       {
00703          return true;
00704       }
00705       else if (pref == rhs.pref)
00706       {
00707          return replacement < rhs.replacement;
00708       }
00709    }
00710    return false;
00711 }
00712 
00713 DnsResult::SRV::SRV() : priority(0), weight(0), port(0)
00714 {
00715     // .kw. member "transport" is not initialized. good default?
00716 }
00717 
00718 bool 
00719 DnsResult::SRV::operator<(const DnsResult::SRV& rhs) const
00720 {
00721    if (naptrpref < rhs.naptrpref)
00722    {
00723       return true;
00724    }
00725    else if(naptrpref == rhs.naptrpref)
00726    {
00727       if (transport < rhs.transport)
00728       {
00729          return true;
00730       }
00731       else if (transport == rhs.transport)
00732       {
00733          if (priority < rhs.priority)
00734          {
00735             return true;
00736          }
00737          else if (priority == rhs.priority)
00738          {
00739             if (weight < rhs.weight)
00740             {
00741                return true;
00742             }
00743             else if (weight == rhs.weight)
00744             {
00745                if (target < rhs.target)
00746                {
00747                   return true;
00748                }
00749             }
00750          }
00751       }
00752    }
00753    return false;
00754 }
00755 
00756 void DnsResult::onDnsResult(const DNSResult<DnsHostRecord>& result)
00757 {
00758    if (!mInterface.isSupported(mTransport, V4) && !mInterface.isSupported(mTransport, V6))
00759    {
00760       return;
00761    }
00762    StackLog (<< "Received dns result for: " << mTarget);
00763    StackLog (<< "DnsResult::onDnsResult() " << result.status);
00764    
00765    // This function assumes that the A query that caused this callback
00766    // is the _only_ outstanding DNS query that might result in a
00767    // callback into this function
00768    if ( mType == Destroyed )
00769    {
00770       destroy();
00771       return;
00772    }
00773 
00774    if (result.status == 0)
00775    {
00776       for (vector<DnsHostRecord>::const_iterator it = result.records.begin(); it != result.records.end(); ++it)
00777       {
00778          in_addr addr;
00779          addr.s_addr = (*it).addr().s_addr;
00780          Tuple tuple(addr, mPort, mTransport, mTarget);
00781          
00782          switch(mInterface.getMarkManager().getMarkType(tuple))
00783          {
00784             case TupleMarkManager::OK:
00785                StackLog (<< "Adding " << tuple << " to result set");
00786                mResults.push_back(tuple);
00787                break;
00788             case TupleMarkManager::GREY:
00789                StackLog(<< "Adding greylisted tuple " << tuple);
00790                mGreylistedTuples.push_back(tuple);
00791                break;
00792             case TupleMarkManager::BLACK:
00793             default:
00794                ;// .bwc. Do nothing.
00795          }
00796       
00797       }
00798       
00799    }
00800    else
00801    {
00802       StackLog (<< "Failed async A query: " << result.msg);
00803    }
00804 
00805    if (mSRVCount == 0)
00806    {
00807       bool changed = (mType == Pending);
00808       if (mResults.empty())
00809       {
00810 #ifdef WIN32_SYNCRONOUS_RESOLUTION_ON_ARES_FAILURE
00811          // Try Windows Name Resolution (not asyncronous)
00812          WSAQUERYSET QuerySet = { 0 };
00813          GUID guidServiceTypeUDP = SVCID_UDP(mPort);
00814          GUID guidServiceTypeTCP = SVCID_TCP(mPort);
00815          HANDLE hQuery;
00816          QuerySet.dwSize = sizeof(WSAQUERYSET);
00817          QuerySet.lpServiceClassId = mTransport == UDP ? &guidServiceTypeUDP : &guidServiceTypeTCP;
00818          QuerySet.dwNameSpace = NS_ALL;
00819          QuerySet.lpszServiceInstanceName = (char *)mTarget.c_str();
00820          if(WSALookupServiceBegin(&QuerySet, LUP_RETURN_ADDR, &hQuery) == 0)
00821          {
00822              DWORD dwQuerySize = 256;   // Starting size
00823              int iRet = 0;
00824              bool fDone = false;
00825              LPWSAQUERYSET pQueryResult = (LPWSAQUERYSET) new char[dwQuerySize];
00826              while(iRet == 0 && pQueryResult)
00827              {
00828                 iRet = WSALookupServiceNext(hQuery, 0, &dwQuerySize, pQueryResult);
00829                 if(pQueryResult && iRet == -1 && GetLastError() == WSAEFAULT)
00830                 {
00831                    delete [] pQueryResult;
00832                    pQueryResult = (LPWSAQUERYSET) new char[dwQuerySize]; // Re-allocate new size
00833                    iRet = WSALookupServiceNext(hQuery, 0, &dwQuerySize, pQueryResult);
00834                 }
00835                 if(pQueryResult && iRet == 0)
00836                 {
00837                    for(DWORD i = 0; i < pQueryResult->dwNumberOfCsAddrs; i++)
00838                    {
00839                       SOCKADDR_IN *pSockAddrIn = (SOCKADDR_IN *)pQueryResult->lpcsaBuffer[i].RemoteAddr.lpSockaddr;
00840                       Tuple tuple(pSockAddrIn->sin_addr, mPort, mTransport, mTarget);
00841                       
00842                       if(mInterface.getMarkManager().getMarkType(tuple)!=TupleMarkManager::BLACK)
00843                       {
00844                         // .bwc. This is the only result we have, so it doesn't
00845                         // matter if it is greylisted.
00846                         StackLog (<< "Adding (WIN) " << tuple << " to result set");
00847                         mResults.push_back(tuple);
00848                         transition(Available);
00849                       }
00850                    
00851                    }
00852                 }
00853              }
00854              delete [] pQueryResult;
00855              WSALookupServiceEnd(hQuery);
00856          }
00857          
00858          if(mResults.empty())
00859          {
00860             if(mSRVResults.empty())
00861             {
00862                if (mGreylistedTuples.empty())
00863                {
00864                   transition(Finished);
00865                   clearCurrPath();
00866                }
00867                else
00868                {
00869                   for(std::vector<Tuple>::iterator i = mGreylistedTuples.begin(); i != mGreylistedTuples.end(); ++i)
00870                   {
00871                      mResults.push_back(*i);
00872                   }
00873                   mGreylistedTuples.clear();
00874                   transition(Available);
00875                }
00876             }
00877             else
00878             {
00879                transition(Available);
00880             }
00881          }
00882 #else
00883          // .bwc. If this A query failed, don't give up if there are more SRVs!
00884          if(mSRVResults.empty())
00885          {
00886             if (mGreylistedTuples.empty())
00887             {
00888                transition(Finished);
00889                clearCurrPath();
00890             }
00891             else
00892             {
00893                for(std::vector<Tuple>::iterator i = mGreylistedTuples.begin(); i != mGreylistedTuples.end(); ++i)
00894                {
00895                   mResults.push_back(*i);
00896                }
00897                mGreylistedTuples.clear();
00898                transition(Available);
00899             }
00900          }
00901          else
00902          {
00903             transition(Available);
00904          }
00905 #endif
00906       }
00907       else 
00908       {
00909          transition(Available);
00910       }
00911       if (changed && mHandler) mHandler->handle(this);
00912    }
00913 }
00914 
00915 void DnsResult::onDnsResult(const DNSResult<DnsAAAARecord>& result)
00916 {
00917 #ifdef USE_IPV6
00918    StackLog (<< "Received AAAA result for: " << mTarget);
00919    if (!mInterface.isSupported(mTransport, V6))
00920    {
00921       return;
00922    }
00923    StackLog (<< "DnsResult::onDnsResult() " << result.status);
00924    assert(mInterface.isSupported(mTransport, V6));
00925 
00926    // This function assumes that the AAAA query that caused this callback
00927    // is the _only_ outstanding DNS query that might result in a
00928    // callback into this function
00929    if ( mType == Destroyed )
00930    {
00931       destroy();
00932       return;
00933    }
00934 
00935    if (result.status == 0)
00936    {
00937       for (vector<DnsAAAARecord>::const_iterator it = result.records.begin(); it != result.records.end(); ++it)
00938       {
00939          Tuple tuple((*it).v6Address(), mPort, mTransport, mTarget);
00940          
00941          switch(mInterface.getMarkManager().getMarkType(tuple))
00942          {
00943             case TupleMarkManager::OK:
00944                StackLog (<< "Adding " << tuple << " to result set");
00945                mResults.push_back(tuple);
00946                break;
00947             case TupleMarkManager::GREY:
00948                StackLog(<< "Adding greylisted tuple " << tuple);
00949                mGreylistedTuples.push_back(tuple);
00950                break;
00951             case TupleMarkManager::BLACK:
00952             default:
00953                ;// .bwc. Do nothing.
00954          }
00955       
00956       }
00957       
00958    }
00959    else
00960    {
00961       StackLog (<< "Failed async AAAA query: " << result.msg);
00962    }
00963    // funnel through to host processing
00964    mDns.lookup<RR_A>(mPassHostFromAAAAtoA, Protocol::Sip, this);
00965 #else
00966    assert(0);
00967 #endif
00968 }
00969 
00970 void DnsResult::onDnsResult(const DNSResult<DnsSrvRecord>& result)
00971 {
00972    StackLog (<< "Received SRV result for: " << mTarget);
00973    assert(mSRVCount>=0);
00974    mSRVCount--;
00975    StackLog (<< "DnsResult::onDnsResult() " << mSRVCount << " status=" << result.status);
00976 
00977    // There could be multiple SRV queries outstanding, but there will be no
00978    // other DNS queries outstanding that might cause a callback into this
00979    // object.
00980    if (mType == Destroyed && mSRVCount == 0)
00981    {
00982       destroy();
00983       return;
00984    }
00985 
00986    if (result.status == 0)
00987    {
00988       for (vector<DnsSrvRecord>::const_iterator it = result.records.begin(); it != result.records.end(); ++it)
00989       {
00990          SRV srv;
00991          srv.key = (*it).name();
00992          srv.priority = (*it).priority();
00993          srv.weight = (*it).weight();
00994          srv.port = (*it).port();
00995          srv.target = (*it).target();
00996          // fillin srv.naptrpref - if found in NAPTR map, then SRV query was driven from a NAPTR lookup
00997          std::map<Data, NAPTR>::iterator itNaptr = mTopOrderedNAPTRs.find(srv.key);
00998          if(itNaptr != mTopOrderedNAPTRs.end())
00999          {
01000             srv.naptrpref = itNaptr->second.pref;
01001          }
01002          else
01003          {
01004             srv.naptrpref = 0;
01005          }
01006          if (srv.key.find("_sips._udp") != Data::npos)
01007          {
01008             srv.transport = DTLS;
01009          }
01010          else if (srv.key.find("_sips._tcp") != Data::npos)
01011          {
01012             srv.transport = TLS;
01013          }
01014          else if (srv.key.find("_udp") != Data::npos)
01015          {
01016             srv.transport = UDP;
01017          }
01018          else if (srv.key.find("_dtls") != Data::npos)
01019          {
01020             srv.transport = DTLS;
01021          }
01022          else if (srv.key.find("_tls") != Data::npos)
01023          {
01024             srv.transport = TLS;
01025          }
01026          else if (srv.key.find("_tcp") != Data::npos)
01027          {
01028             srv.transport = TCP;
01029          }
01030          else
01031          {
01032             StackLog (<< "Skipping SRV " << srv.key);
01033             continue;
01034          }
01035          
01036          if(!mHaveChosenTransport || srv.transport==mTransport)
01037          {
01038             // .bwc. If we have not committed to a given transport, or we have 
01039             // committed to a given transport which this SRV matches, we will
01040             // add this SRV. We do not add SRVs that do not match a transport
01041             // we have committed to.
01042             mSRVResults.push_back(srv);
01043          }
01044       }
01045    }
01046    else
01047    {
01048       StackLog (<< "SRV lookup failed: " << result.domain << " " << result.status);
01049    }
01050 
01051    // no outstanding queries 
01052    if (mSRVCount == 0) 
01053    {
01054       if (mSRVResults.empty())
01055       {
01056          if (mTransport == UNKNOWN_TRANSPORT)
01057          {
01058             if (mSips)
01059             {
01060                mTransport = TLS;
01061                mHaveChosenTransport=true;
01062                mPort = Symbols::DefaultSipsPort;
01063             }
01064             else
01065             {
01066                if (mInterface.isSupported(UDP, V4))
01067                {
01068                   mTransport = UDP;
01069                   mHaveChosenTransport=true;
01070                }
01071                else if (mInterface.isSupported(TCP, V4))
01072                {
01073                   mTransport = TCP;
01074                   mHaveChosenTransport=true;
01075                }
01076                /* Yes, there is the possibility that at this point mTransport
01077                   is still UNKNOWN_TRANSPORT, but this is likely to fail just as
01078                   well as defaulting to UDP when there isn't an interface that
01079                   supports UDP.
01080                   It doesn't support failover to TCP when there is a UDP failure,
01081                   but neither does the original code.
01082                   This fixes the case where there is no UDP transport, but
01083                   there was no explicit ;transport=tcp on the uri.
01084                   (mjf)
01085                 */
01086                mPort = Symbols::DefaultSipPort;
01087             }
01088          }
01089          else
01090          {
01091             mPort = getDefaultPort(mTransport, 0);
01092          }
01093          
01094          StackLog (<< "No SRV records for " << mTarget << ". Trying A records");
01095          if (mInterface.isSupported(mTransport, V6) || mInterface.isSupported(mTransport, V4))
01096          {
01097             lookupHost(mTarget);
01098          }
01099          else
01100          {
01101             primeResults();
01102          }
01103       }
01104       else
01105       {
01106          std::sort(mSRVResults.begin(),mSRVResults.end()); // !jf! uggh
01107          primeResults();
01108       }
01109    }
01110 }
01111 
01112 static Data enumService1("e2u+sip");
01113 static Data enumService2("sip+e2u");
01114 void
01115 DnsResult::onEnumResult(const DNSResult<DnsNaptrRecord>& result)
01116 {
01117    mDoingEnum = false;
01118    
01119    if (result.status == 0)
01120    {
01121       DnsNaptrRecord best;
01122       best.order() = -1;
01123 
01124       for (vector<DnsNaptrRecord>::const_iterator i = result.records.begin(); i != result.records.end(); ++i)
01125       {
01126          InfoLog (<< "service=" << i->service()
01127                   << " order=" << i->order()
01128                   << " flags="  << i->flags() 
01129                   << " regexp substitution=" << i->regexp().replacement()
01130                   << " replacement=" << i->replacement());
01131 
01132          if ( (isEqualNoCase(i->service(), enumService1) ||
01133                isEqualNoCase(i->service(), enumService2) )  && // only E2U records
01134               //i->flags().find("u") != Data::npos && // must be terminal record
01135               i->replacement().empty() )
01136                
01137          {
01138             if (best.order() == -1)
01139             {
01140                best = *i;
01141             }
01142             else if (i->order() < best.order())
01143             {
01144                best = *i;
01145             }
01146             else if (i->order() == best.order() && 
01147                      i->preference() < best.preference())
01148             {
01149                best = *i;
01150             }
01151          }
01152       }
01153       
01154       if (best.order() != -1)
01155       {
01156          InfoLog (<< "Found an enum result: " << best.regexp().replacement());
01157          try
01158          {
01159             Uri rewrite(best.regexp().apply(Data::from(mInputUri)));
01160             InfoLog (<< "Rewrote uri " << mInputUri << " -> " << rewrite);
01161             mHandler->rewriteRequest(rewrite);
01162             lookupInternal(rewrite);
01163          }
01164          catch (ParseException&  e )
01165          {
01166             ErrLog(<<"Caught exception: "<< e);
01167             lookupInternal(mInputUri);
01168          }
01169       }
01170       else
01171       {
01172          lookupInternal(mInputUri);
01173       }
01174    }
01175    else
01176    {
01177       lookupInternal(mInputUri);
01178    }
01179 }
01180 
01181 void
01182 DnsResult::onNaptrResult(const DNSResult<DnsNaptrRecord>& result)
01183 {
01184    bool bFail = false;
01185    if (result.status == 0)
01186    {
01187       int preferredNAPTROrder=65536; // order is unsigned short - so max is 65535, initialize to one higher
01188       std::list<NAPTR> supportedNAPTRs;
01189       for (vector<DnsNaptrRecord>::const_iterator it = result.records.begin(); it != result.records.end(); ++it)
01190       {
01191          NAPTR naptr;
01192          naptr.key = (*it).name();
01193          naptr.flags = (*it).flags();
01194          naptr.order = (*it).order();
01195          naptr.pref = (*it).preference();
01196          naptr.regex = (*it).regexp();
01197          naptr.replacement = (*it).replacement();
01198          naptr.service = (*it).service();
01199          
01200          StackLog (<< "Received NAPTR record: " << naptr);
01201          
01202          if ( !mSips || naptr.service.find("SIPS") == 0)
01203          {
01204             if (mInterface.isSupported(naptr.service))
01205             {
01206                supportedNAPTRs.push_back(naptr);
01207                if(naptr.order < preferredNAPTROrder)
01208                {               
01209                   preferredNAPTROrder = naptr.order;
01210                }
01211             }
01212          }
01213       }
01214 
01215       // This means that dns / NAPTR is misconfigured for this client 
01216       if (supportedNAPTRs.empty())
01217       {
01218          StackLog (<< "There are no NAPTR records supported by this client so do an SRV lookup instead");
01219          bFail = true;
01220       }
01221       else
01222       {
01223          // Go through NAPTR's and issue SRV queries for each supported record, that has equal
01224          // ordering to the most preferred order discovered above
01225          transition(Pending);
01226          for (std::list<NAPTR>::const_iterator it = supportedNAPTRs.begin(); it != supportedNAPTRs.end(); ++it)
01227          {
01228             if(preferredNAPTROrder == (*it).order)
01229             {
01230                StackLog (<< "NAPTR record is supported and matches highes priority order. doing SRV query: " << (*it));
01231                mTopOrderedNAPTRs[(*it).replacement] = (*it);
01232                mSRVCount++;
01233                mDns.lookup<RR_SRV>((*it).replacement, Protocol::Sip, this);
01234             }
01235          }
01236       }
01237    }
01238    else
01239    {
01240       if (result.status > 6)
01241       {
01242          DebugLog (<< "NAPTR lookup failed: " << result.domain << " " << result.msg);
01243       }
01244       else
01245       {
01246          StackLog (<< "NAPTR lookup failed: " << result.domain << " " << result.msg);
01247       }
01248       bFail = true;
01249    }
01250 
01251    if (bFail)
01252    {
01253       if (mSips)
01254       {
01255          if (!mInterface.isSupportedProtocol(TLS))
01256          {
01257             transition(Finished);
01258             if (mHandler) mHandler->handle(this);
01259             return;
01260          }
01261 
01262          mSRVCount++;
01263          mDns.lookup<RR_SRV>("_sips._tcp." + mTarget, Protocol::Sip, this);
01264          StackLog (<< "Doing SRV lookup of _sips._tcp." << mTarget);
01265       }
01266       else
01267       {
01268          if (mInterface.isSupportedProtocol(TLS))
01269          {
01270             mDns.lookup<RR_SRV>("_sips._tcp." + mTarget, Protocol::Sip, this);
01271             ++mSRVCount;
01272             StackLog (<< "Doing SRV lookup of _sips._tcp." << mTarget);
01273          }
01274          if (mInterface.isSupportedProtocol(DTLS))
01275          {
01276             mDns.lookup<RR_SRV>("_sips._udp." + mTarget, Protocol::Sip, this);
01277             ++mSRVCount;
01278             StackLog (<< "Doing SRV lookup of _sips._udp." << mTarget);
01279          }
01280          if (mInterface.isSupportedProtocol(TCP))
01281          {
01282             mDns.lookup<RR_SRV>("_sip._tcp." + mTarget, Protocol::Sip, this);
01283             ++mSRVCount;
01284             StackLog (<< "Doing SRV lookup of _sip._tcp." << mTarget);
01285          }
01286          if (mInterface.isSupportedProtocol(UDP))
01287          {
01288             mDns.lookup<RR_SRV>("_sip._udp." + mTarget, Protocol::Sip, this);
01289             ++mSRVCount;
01290             StackLog (<< "Doing SRV lookup of _sip._udp." << mTarget);
01291          }
01292       }
01293    }
01294 }
01295 
01296 void 
01297 DnsResult::onDnsResult(const DNSResult<DnsNaptrRecord>& result)
01298 {
01299    StackLog (<< "Received NAPTR result for: " << mInputUri << " target=" << mTarget);
01300    StackLog (<< "DnsResult::onDnsResult() " << result.status);
01301 
01302    // This function assumes that the NAPTR query that caused this
01303    // callback is the ONLY outstanding query that might cause
01304    // a callback into this object
01305    if (mType == Destroyed)
01306    {
01307       destroy();
01308       return;
01309    }
01310 
01311    if (mDoingEnum)
01312    {
01313       onEnumResult(result);
01314    }
01315    else
01316    {
01317       onNaptrResult(result);
01318    }
01319   
01320 }
01321 
01322 void DnsResult::onDnsResult(const DNSResult<DnsCnameRecord>& result)
01323 {
01324 }
01325 
01326 void DnsResult::clearCurrPath()
01327 {
01328    while (!mCurrentPath.empty())
01329    {
01330       mCurrentPath.pop_back();
01331    }
01332 }
01333 
01334 EncodeStream& 
01335 resip::operator<<(EncodeStream& strm, const resip::DnsResult& result)
01336 {
01337    strm << result.mTarget << " --> " << Inserter(result.mResults);
01338    return strm;
01339 }
01340 
01341 
01342 EncodeStream& 
01343 resip::operator<<(EncodeStream& strm, const resip::DnsResult::NAPTR& naptr)
01344 {
01345    strm << "key=" << naptr.key
01346         << " order=" << naptr.order
01347         << " pref=" << naptr.pref
01348         << " flags=" << naptr.flags
01349         << " service=" << naptr.service
01350         << " regex=" << naptr.regex.regexp() << " -> " << naptr.regex.replacement()
01351         << " replacement=" << naptr.replacement;
01352    return strm;
01353 }
01354 
01355 EncodeStream& 
01356 resip::operator<<(EncodeStream& strm, const resip::DnsResult::SRV& srv)
01357 {
01358    strm << "key=" << srv.key
01359         << " t=" << Tuple::toData(srv.transport) 
01360         << " p=" << srv.priority
01361         << " w=" << srv.weight
01362         << " port=" << srv.port
01363         << " target=" << srv.target;
01364    return strm;
01365 }
01366 
01367 //  Copyright (c) 2003, Jason Fischl 
01368 /* ====================================================================
01369  * The Vovida Software License, Version 1.0 
01370  * 
01371  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
01372  * 
01373  * Redistribution and use in source and binary forms, with or without
01374  * modification, are permitted provided that the following conditions
01375  * are met:
01376  * 
01377  * 1. Redistributions of source code must retain the above copyright
01378  *    notice, this list of conditions and the following disclaimer.
01379  * 
01380  * 2. Redistributions in binary form must reproduce the above copyright
01381  *    notice, this list of conditions and the following disclaimer in
01382  *    the documentation and/or other materials provided with the
01383  *    distribution.
01384  * 
01385  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
01386  *    and "Vovida Open Communication Application Library (VOCAL)" must
01387  *    not be used to endorse or promote products derived from this
01388  *    software without prior written permission. For written
01389  *    permission, please contact vocal@vovida.org.
01390  *
01391  * 4. Products derived from this software may not be called "VOCAL", nor
01392  *    may "VOCAL" appear in their name, without prior written
01393  *    permission of Vovida Networks, Inc.
01394  * 
01395  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
01396  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
01397  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
01398  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
01399  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
01400  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
01401  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
01402  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
01403  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
01404  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
01405  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
01406  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
01407  * DAMAGE.
01408  * 
01409  * ====================================================================
01410  * 
01411  * This software consists of voluntary contributions made by Vovida
01412  * Networks, Inc. and many individuals on behalf of Vovida Networks,
01413  * Inc.  For more information on Vovida Networks, Inc., please see
01414  * <http://www.vovida.org/>.
01415  *
01416  */