< Previous by Date | Date Index | Next by Date > |
< Previous in Thread | Thread Index | Next in Thread > |
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/>. * */