#include "mind.h"
#include "dnspacket.h"
#include "rdata_a.h"
#include "rdata_name.h"
#include "resolver.h"
#include "rrfactory.h"
#include "watchptr.h"
#include "wwwi/singleton.h"
using std::auto_ptr;
using std::memset;
using WWWI::IPDump;
using WWWI::GetSingleton;
Resolver::Resolver() {
m_cap = GetSingleton<RRCache>();
m_usID = 0;
}
ResponsePtr Resolver::QueryAuthority(QuestionConstPtr ci_qnp, DNSSocketPtr i_dspAuthority, IPAddress i_ipAuthority) {
DNSPacketPtr dppServer;
unsigned short usID;
SockAddrIn *sipAuthority;
LogHandle lh(LF_RESOLVER,LY_DEBUG);
lh << "resolv qauth start " << ci_qnp << endl; lh();
sipAuthority = new SockAddrIn;
memset(sipAuthority,0,sizeof(SockAddrIn));
sipAuthority->sin_addr.s_addr = i_ipAuthority;
sipAuthority->sin_port = htons(53);
sipAuthority->sin_family = AF_INET;
usID = m_usID++;
dppServer = new DNSPacket(sipAuthority,usID,ci_qnp);
i_dspAuthority->SendPacket(dppServer);
SoftDelete(dppServer);
dppServer = i_dspAuthority->RecvPacketTimed(1000);
if (dppServer==NULL) {
lh << "resolv qauth timeout " << ci_qnp << endl;
return NULL;
}
if (dppServer->TestID(usID)==false) {
lh << "resolv qauth xtalk " << ci_qnp << endl;
SoftDelete(dppServer);
return NULL;
}
RequestPtr rqp = NULL;
ResponsePtr rsp = NULL;
WatchPtr<RequestPtr> watch_rqp(rqp);
WatchPtr<ResponsePtr> watch_rsp(rsp);
dppServer->Parse(usID,rqp,rsp);
SoftDelete(dppServer);
if ((rqp->GetOpcode()!=OPCODE_QUERY)||
(rqp->GetQuestionCount()!=1)||
(rsp==NULL)) {
lh << "resolv qauth gibberish " << ci_qnp << endl;
return NULL;
}
lh << "resolv qauth done " << ci_qnp << endl;
watch_rsp.Release();
return rsp;
}
ResponsePtr Resolver::QueryRecursive(const char *i_strName, RRType i_ty, RRClass i_cl, unsigned i_uSteps) {
QuestionPtr qnp = new Question(i_strName,i_ty,i_cl);
ResponsePtr rsp = this->QueryRecursive(qnp,i_uSteps);
return rsp;
}
ResponsePtr Resolver::QueryRecursive(QuestionConstPtr ci_qnp,unsigned i_uSteps) {
ResponsePtr rsp;
auto_ptr<DNSSocket> dsuUpstream(new DNSSocket(INADDR_ANY,0));
bool bCached;
if (i_uSteps==0) return new Response(RCODE_SERVFAIL);
rsp = m_cap->Get(ci_qnp);
bCached = (rsp!=NULL);
while(bCached==false) {
RRListPtr rlpAuthority;
rlpAuthority = m_cap->GetBestAuthority(ci_qnp->GetName());
while(i_uSteps>0) {
i_uSteps--;
RRList::iterator rrpi;
IPAddress ipAuthority;
if (rlpAuthority->size()==0) return new Response(RCODE_SERVFAIL);
rrpi = rlpAuthority->GetRandomRR();
ipAuthority = SimpleResolve((*rrpi)->GetRData()->As<RDataName>()->GetRDName(),i_uSteps);
if (ipAuthority==(IPAddress)-1) {
SoftDelete(*rrpi);
rlpAuthority->erase(rrpi);
continue;
}
rsp = this->QueryAuthority(ci_qnp,dsuUpstream.get(),ipAuthority);
if (rsp==NULL) continue;
if (rsp->GetRCode()==RCODE_SERVFAIL) {
SoftDelete(*rrpi);
rlpAuthority->erase(rrpi);
continue;
}
if ((rsp->GetANCount()==0)&&(rsp->GetNSCount()==0)) {
SoftDelete(*rrpi);
rlpAuthority->erase(rrpi);
continue;
}
break;
}
SoftDelete(rlpAuthority);
if ((i_uSteps==0)&&(rsp==NULL)) return new Response(RCODE_SERVFAIL);
if (rsp->GetRCode()!=RCODE_NOERROR) break;
if (rsp->GetAA()==true) break;
if (rsp->GetANCount()==0) {
m_cap->Add(rsp);
SoftDelete(rsp);
continue;
}
break;
}
if (rsp!=NULL) {
if (bCached==false) {
if ((rsp->GetAA()==true)&&(rsp->GetTC()==false)) m_cap->Add(rsp);
}
if (rsp->GetANList()->HasQuestionMatch(ci_qnp)==false) {
if (rsp->GetANCount()==1) {
RRPtr rrp = *rsp->GetANList()->begin();
if (rrp->IsTypeClass(TY_CNAME,ci_qnp->GetClass())==true) {
QuestionPtr qnpCName = new Question(*ci_qnp,rrp->GetRData()->As<RDataName>()->GetRDName());
ResponsePtr rspCName = this->QueryRecursive(qnpCName,i_uSteps-1);
SoftDelete(qnpCName);
if (rspCName!=NULL) {
rrp = RRFactory::NewRR(rrp);
rspCName->AddAN(rrp);
SoftDelete(rsp);
rsp = rspCName;
}
}
}
}
}
return rsp;
}
IPAddress Resolver::SimpleResolve(const char *ci_strName, unsigned i_uSteps) {
IPAddress ipOut = inet_addr(ci_strName);
if (ipOut==(IPAddress)-1) {
LabelListPtr llp = new LabelList(ci_strName);
ipOut = this->SimpleResolve(llp,i_uSteps);
SoftDelete(llp);
} else {
LogHandle lh(LF_RESOLVER,LY_DEBUG);
lh << "resolv simple fast " << ci_strName << " is ";
IPDump(lh,ipOut);
}
return ipOut;
}
inline IPAddress Resolver::SimpleResolve(LabelListConstPtr ci_llpName, unsigned i_uSteps) {
RRListPtr rlp;
IPAddress ipOut;
LogHandle lh(LF_RESOLVER,LY_DEBUG);
if (i_uSteps==0) return (IPAddress)-1;
lh << "resolv simple start " << ci_llpName << endl; lh();
rlp = m_cap->GetSimple(ci_llpName,TY_A,CL_IN);
if (rlp == NULL) {
QuestionPtr qnp;
ResponsePtr rsp;
LabelListPtr llp;
llp = new LabelList(*ci_llpName);
qnp = new Question(llp,TY_A,CL_IN);
rsp = this->QueryRecursive(qnp,i_uSteps);
if (rsp==NULL) goto srFailed;
rlp = rsp->RemoveANList();
SoftDelete(rsp);
SoftDelete(qnp);
if (rlp->size()==0) goto srFailed;
}
assert(rlp->size()>0);
ipOut = (*rlp->GetRandomRR())->GetRData()->As<RDataA>()->GetRDAddress();
SoftDelete(rlp);
lh << "resolv simple done " << ci_llpName << " is ";
IPDump(lh,ipOut);
lh();
return ipOut;
srFailed:
lh << "resolv simple fail " << ci_llpName << endl; lh();
return (IPAddress)-1;
}