along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-// $Id: dnspacket.cc,v 1.9 2003/01/10 18:43:01 ahu Exp $
+// $Id: dnspacket.cc,v 1.10 2003/01/11 21:09:57 ahu Exp $
#include "utility.hh"
#include <cstdio>
p[2]=0;
p[3]=1; // IN
- u_int32_t *ttlp=(u_int32_t *)(p+4);
-
- *ttlp=htonl(ttl); // 4, 5, 6, 7
-
+ putLong(p+4, ttl);
p[8]=0;
p[9]=4; // length of data
- p[10]=(ip>>24)&0xff;
- p[11]=(ip>>16)&0xff;
- p[12]=(ip>>8)&0xff;
- p[13]=ip&0xff;
-
+ putLong(p+10,ip);
stringbuffer.append(piece1);
stringbuffer.append(p,14);
p[2]=0;
p[3]=1; // IN
- u_int32_t *ttlp=(u_int32_t *)(p+4);
-
- *ttlp=htonl(ttl); // 4, 5, 6, 7
-
+ putLong(p+4,ttl);
p[8]=0;
p[9]=16; // length of data
piece2[2]=0;
piece2[3]=1; // IN
- u_int32_t *ttlp=(u_int32_t *)(piece2+4);
- *ttlp=htonl(ttl); // 4, 5, 6, 7
-
+ putLong(piece2+4,ttl);
piece2[8]=0;
piece2[9]=0; // need to fill this in
string DNSPacket::serializeSOAData(const SOAData &d)
{
ostringstream o;
-
// nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
-
o<<d.nameserver<<" "<< d.hostmaster <<" "<< d.serial <<" "<< d.refresh << " "<< d.retry << " "<< d.expire << " "<< d.default_ttl;
return o.str();
-
}
/* the hostmaster is encoded as two parts - the bit UNTIL the first unescaped '.'
p[2]=0;
p[3]=1; // IN
-
- u_int32_t *ttlp=(u_int32_t *)(p+4);
- *ttlp=htonl(ttl); // 4, 5, 6, 7
-
+ putLong(p+4,ttl);
p[8]=0;
p[9]=0; // need to fill this in (length)
*i_p++=htonl(soadata.expire);
*i_p++=htonl(soadata.default_ttl);
-
p[9]=piece3.length()+piece4.length()+20;
stringbuffer+=piece1;
d.nscount++;
}
-
-
-
void DNSPacket::addCNAMERecord(const DNSResourceRecord &rr)
{
addCNAMERecord(rr.qname, rr.content, rr.ttl);
{
string piece1;
- //xtoqname(domain.c_str(),&piece1);
toqname(domain.c_str(),&piece1);
char p[10];
p[2]=0;
p[3]=1; // IN
- u_int32_t *ttlp=(u_int32_t *)(p+4);
-
- *ttlp=htonl(ttl); // 4, 5, 6, 7
-
+ putLong(p+4,ttl);
p[8]=0;
p[9]=0; // need to fill this in
p[2]=0;
p[3]=1; // IN
- u_int32_t *ttlp=(u_int32_t *)(p+4);
-
- *ttlp=htonl(ttl); // 4, 5, 6, 7
-
+ putLong(p+4,ttl);
+
p[8]=0;
p[9]=0; // need to fill this in
}
+void DNSPacket::makeHeader(char *p,u_int16_t qtype, u_int32_t ttl)
+{
+ p[0]=0;
+ p[1]=qtype;
+ p[2]=0;
+ p[3]=1; // IN
+ putLong(p+4,ttl);
+ p[8]=0;
+ p[9]=0; // need to fill this in
+}
void DNSPacket::addNAPTRRecord(const string &domain, const string &content, u_int32_t ttl)
{
//xtoqname(domain.c_str(),&piece1);
toqname(domain.c_str(),&piece1);
char p[10];
-
- p[0]=0;
- p[1]=QType::NAPTR;
- p[2]=0;
- p[3]=1; // IN
-
- u_int32_t *ttlp=(u_int32_t *)(p+4);
-
- *ttlp=htonl(ttl); // 4, 5, 6, 7
-
- p[8]=0;
- p[9]=0; // need to fill this in
+ makeHeader(p,QType::NAPTR,ttl);
// content contains: 100 100 "s" "http+I2R" "" _http._tcp.foo.com.
{
string piece1;
- //xtoqname(domain,&piece1);
toqname(domain,&piece1);
char p[10];
-
- p[0]=0;
- p[1]=12; // PTR
- p[2]=0;
- p[3]=1; // IN
-
- u_int32_t *ttlp=(u_int32_t *)(p+4);
-
- *ttlp=htonl(ttl); // 4, 5, 6, 7
-
- p[8]=0;
- p[9]=0; // need to fill this in
+ makeHeader(p,QType::PTR,ttl);
-
string piece3;
- //xtoqname(alias,&piece3);
toqname(alias,&piece3);
p[9]=piece3.length();
//xtoqname(domain, &piece1);
toqname(domain, &piece1);
char p[10];
-
- p[0]=0;
- p[1]=16; // TXT
- p[2]=0;
- p[3]=1; // IN
-
- u_int32_t *ttlp=(u_int32_t *)(p+4);
-
- *ttlp=htonl(ttl); // 4, 5, 6, 7
-
- p[8]=0;
- p[9]=0; // need to fill this in
-
+ makeHeader(p,QType::TXT,ttl);
string piece3;
piece3.reserve(txt.length()+1);
piece3.append(1,txt.length());
d.ancount++;
}
-
void DNSPacket::addHINFORecord(const DNSResourceRecord& rr)
{
addHINFORecord(rr.qname, rr.content, rr.ttl);
p[1]=13; // HINFO
p[2]=0;
p[3]=1; // IN
-
- u_int32_t *ttlp=(u_int32_t *)(p+4);
-
- *ttlp=htonl(ttl); // 4, 5, 6, 7
+ putLong(p+4,ttl);
p[8]=0;
p[9]=0; // need to fill this in
p[1]=2; // NS
p[2]=0;
p[3]=1; // IN
-
- u_int32_t *ttlp=(u_int32_t *)(p+4);
-
- *ttlp=htonl(ttl); // 4, 5, 6, 7
+ putLong(p+4,ttl);
p[8]=0;
p[9]=0; // need to fill this in
break;
default:
- L<<Logger::Warning<<"Unable to insert a record of type "<<rr.qtype.getName()<<" for '"<<rr.qname<<"'"<<endl;
+ if(rr.qtype.getCode()>1024)
+ addGenericRecord(rr);
+ else
+ L<<Logger::Warning<<"Unable to insert a record of type "<<rr.qtype.getName()<<" for '"<<rr.qname<<"'"<<endl;
}
}
d.ancount=htons(d.ancount);
len=stringbuffer.length();
}
+void DNSPacket::addGenericRecord(const DNSResourceRecord& rr)
+{
+ string piece1;
+ //xtoqname(domain, &piece1);
+ toqname(rr.qname, &piece1);
+ char p[10];
+
+ p[0]=0;
+ p[1]=rr.qtype.getCode()-1024; // TXT
+ p[2]=0;
+ p[3]=1; // IN
+
+ putLong(p+4,rr.ttl);
+
+ p[8]=rr.content.length()/256;
+ p[9]=rr.content.length()%256; // need to fill this in
+
+ stringbuffer+=piece1;
+ stringbuffer.append(p,10);
+ stringbuffer+=rr.content;
+ if(rr.d_place==DNSResourceRecord::ADDITIONAL)
+ d.arcount++;
+ else
+ d.ancount++;
+}
/** Truncates a packet that has already been wrapup()-ed, possibly via a call to getData(). Do not call this function
before having done this - it will possibly break your packet, or crash your program.
rr.content=tmp;
break;
-
default:
- throw AhuException("Unknown type number "+itoa(rr.qtype.getCode())+" for: '"+rr.qname+"'");
+ rr.qtype=rr.qtype.getCode()+1024;
+ rr.content.assign((const char *)datapos,length);
+ // throw AhuException("Unknown type number "+itoa(rr.qtype.getCode())+" for: '"+rr.qname+"'");
}
if(pos<ntohs(d.ancount))
rr.d_place=DNSResourceRecord::ANSWER;
cache_t cache;
map<string,string> negcache;
+struct GetBestNSAnswer
+{
+ string qname;
+ set<DNSResourceRecord> bestns;
+ bool operator<(const GetBestNSAnswer &b) const
+ {
+ if(qname<b.qname)
+ return true;
+ if(qname==b.qname)
+ return bestns<b.bestns;
+ return false;
+ }
+};
+
/** dramatis personae */
-int doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret,int depth=0);
-int doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth=0);
+int doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret,
+ int depth, set<GetBestNSAnswer>&beenthere);
+int doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere);
bool doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res);
bool doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res);
-void getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth);
+void getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth, set<GetBestNSAnswer>& beenthere);
void addCruft(const string &qname, vector<DNSResourceRecord>& ret);
-string getBestNSNamesFromCache(const string &qname,set<string>& nsset, int depth);
+string getBestNSNamesFromCache(const string &qname,set<string>& nsset, int depth, set<GetBestNSAnswer>&beenthere);
void addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth);
/** everything begins here - this is the entry point just after receiving a packet */
int beginResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret)
{
- int res=doResolve(qname, qtype, ret,0);
+ set<GetBestNSAnswer> beenthere;
+ int res=doResolve(qname, qtype, ret,0,beenthere);
if(!res)
addCruft(qname, ret);
cout<<endl;
return res;
}
-int doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth)
+int doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere)
{
string prefix;
prefix.assign(3*depth, ' ');
string subdomain(qname);
set<string> nsset;
- subdomain=getBestNSNamesFromCache(subdomain,nsset,depth);
- if(!(res=doResolveAt(nsset,subdomain,qname,qtype,ret,depth)))
+ subdomain=getBestNSNamesFromCache(subdomain,nsset,depth, beenthere); // pass beenthere to both occasions/
+ if(!(res=doResolveAt(nsset,subdomain,qname,qtype,ret,depth, beenthere)))
return 0;
cout<<prefix<<qname<<": failed"<<endl;
return res<0 ? RCode::ServFail : res;
}
-string getA(const string &qname, int depth=0)
+string getA(const string &qname, int depth, set<GetBestNSAnswer>& beenthere)
{
vector<DNSResourceRecord> res;
string ret;
- if(!doResolve(qname,QType(QType::A), res,depth+1) && !res.empty())
+ if(!doResolve(qname,QType(QType::A), res,depth+1,beenthere) && !res.empty())
ret=res[res.size()-1].content; // last entry, in case of CNAME in between
return ret;
}
-void getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth)
+int getCache(const string &qname, const QType& qt, set<DNSResourceRecord>* res=0)
+{
+ cache_t::const_iterator j=cache.find(toLower(qname)+"|"+qt.getName());
+ if(j!=cache.end() && j->first==toLower(qname)+"|"+qt.getName() && j->second.begin()->ttl>(unsigned int)time(0)) {
+ if(res)
+ *res=j->second;
+ return (unsigned int)j->second.begin()->ttl-time(0);
+ }
+ return -1;
+}
+
+void replaceCache(const string &tuple, const set<DNSResourceRecord>& content)
+{
+ cache[tuple]=content;
+}
+
+
+void getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth, set<GetBestNSAnswer>& beenthere)
{
string prefix, subdomain(qname);
prefix.assign(3*depth, ' ');
do {
cout<<prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl;
-
- cache_t::const_iterator j=cache.find(toLower(subdomain)+"|NS");
- if(j!=cache.end() && j->first==toLower(subdomain)+"|NS") {
- for(set<DNSResourceRecord>::const_iterator k=j->second.begin();k!=j->second.end();++k) {
- if(k->ttl>(unsigned int)time(0)) { // the below is fugly
- if(!endsOn(k->content,subdomain) || // glue risk
- (cache.find(toLower(k->content)+"|A")!=cache.end() && ((time_t)cache[toLower(k->content)+"|A"].begin()->ttl-time(0))>5)) {
+ set<DNSResourceRecord>ns;
+ if(getCache(subdomain,QType(QType::NS),&ns)>0) {
+ for(set<DNSResourceRecord>::const_iterator k=ns.begin();k!=ns.end();++k) {
+ if(k->ttl>(unsigned int)time(0)) {
+ set<DNSResourceRecord>aset;
+ if(!endsOn(k->content,subdomain) || getCache(k->content,QType(QType::A),&aset) > 5) {
bestns.insert(*k);
cout<<prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<k->content<<"'"<<endl;
cout<<prefix<<qname<<": endson: "<<endsOn(k->content,subdomain);
- if(cache.find(toLower(k->content)+"|A")!=cache.end())
- cout<<", in cache, ttl="<<((time_t)cache[toLower(k->content)+"|A"].begin()->ttl-time(0))<<endl;
+ if(!aset.empty())
+ cout<<", in cache, ttl="<<((time_t)aset.begin()->ttl-time(0))<<endl;
else
cout<<", not in cache"<<endl;
}
else
- cout<<prefix<<qname<<": NS in cache for '"<<subdomain<<"' , but needs glue ("<<k->content<<") which we miss or is expired"<<endl;
+ cout<<prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<k->content<<") which we miss or is expired"<<endl;
}
}
if(!bestns.empty()) {
- cout<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"'"<<endl;
- return;
+ GetBestNSAnswer answer;
+ answer.qname=toLower(qname); answer.bestns=bestns;
+ if(beenthere.count(answer)) {
+ cout<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' but part of LOOP! Trying less specific NS"<<endl;
+ for(set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j)
+ cout<<prefix<<qname<<": beenthere: "<<j->qname<<" ("<<j->bestns.size()<<")"<<endl;
+ bestns.clear();
+ }
+ else {
+ beenthere.insert(answer);
+ cout<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"'"<<endl;
+ return;
+ }
}
}
}while(chopOff(subdomain));
void addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth)
{
set<DNSResourceRecord> bestns;
- getBestNSFromCache(qname, bestns, depth);
+ set<GetBestNSAnswer>beenthere;
+ getBestNSFromCache(qname, bestns, depth,beenthere);
for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
DNSResourceRecord ns=*k;
ns.d_place=DNSResourceRecord::AUTHORITY;
ret.push_back(ns);
}
}
-
-string getBestNSNamesFromCache(const string &qname,set<string>& nsset, int depth)
+/** doesn't actually do the work, leaves that to getBestNSFromCache */
+string getBestNSNamesFromCache(const string &qname,set<string>& nsset, int depth, set<GetBestNSAnswer>&beenthere)
{
string subdomain(qname);
set<DNSResourceRecord> bestns;
- getBestNSFromCache(subdomain, bestns, depth);
+ getBestNSFromCache(subdomain, bestns, depth, beenthere);
for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
nsset.insert(k->content);
prefix.assign(3*depth, ' ');
cout<<prefix<<qname<<": Looking for CNAME cache hit of '"<<tuple<<"'"<<endl;
- cache_t::const_iterator i=cache.find(tuple);
- if(i!=cache.end() && i->first==tuple) { // found it
- for(set<DNSResourceRecord>::const_iterator j=i->second.begin();j!=i->second.end();++j) {
+ set<DNSResourceRecord> cset;
+ if(getCache(qname,QType(QType::CNAME),&cset) > 0) {
+ for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
if(j->ttl>(unsigned int)time(0)) {
- cout<<prefix<<qname<<": Found cache CNAME hit for '"<<tuple<<"' to '"<<i->second.begin()->content<<"'"<<endl;
+ cout<<prefix<<qname<<": Found cache CNAME hit for '"<<tuple<<"' to '"<<j->content<<"'"<<endl;
DNSResourceRecord rr=*j;
rr.ttl-=time(0);
ret.push_back(rr);
- if(!(qtype==QType(QType::CNAME))) // perhaps they really wanted a CNAME!
- res=doResolve(i->second.begin()->content, qtype, ret, depth);
+ if(!(qtype==QType(QType::CNAME))) {// perhaps they really wanted a CNAME!
+ set<GetBestNSAnswer>beenthere;
+ res=doResolve(j->content, qtype, ret, depth, beenthere);
+ }
return true;
}
}
tuple=ni->second+"|SOA";
}
- cache_t::const_iterator i=cache.find(tuple);
+ set<DNSResourceRecord> cset;
bool found=false, expired=false;
- if(i!=cache.end() && i->first==tuple) { // found it
+ if(getCache(qname,qtype,&cset)>0) {
cout<<prefix<<qname<<": Found cache hit for "<<qtype.getName()<<": ";
- for(set<DNSResourceRecord>::const_iterator j=i->second.begin();j!=i->second.end();++j) {
+ for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
cout<<j->content;
if(j->ttl>(unsigned int)time(0)) {
DNSResourceRecord rr=*j;
}
/** returns -1 in case of no results, rcode otherwise */
-int doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth)
+int doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret,
+ int depth, set<GetBestNSAnswer>&beenthere)
{
string prefix;
prefix.assign(3*depth, ' ');
LWRes r;
LWRes::res_t result;
- cout<<prefix<<qname<<": Cache consultations done, going on the wire!"<<endl;
+ cout<<prefix<<qname<<": Cache consultations done, have "<<nameservers.size()<<" NS to contact"<<endl;
for(;;) { // we may get more specific nameservers
bool aabit=false;
cout<<prefix<<qname<<": Not using NS to resolve itself!"<<endl;
continue;
}
- cout<<prefix<<qname<<": Trying to resolve NS "<<*tns<<endl;
- string remoteIP=getA(*tns, depth+1);
+ cout<<prefix<<qname<<": Trying to resolve NS "<<*tns<<" ("<<1+tns-rnameservers.begin()<<"/"<<rnameservers.size()<<")"<<endl;
+ string remoteIP=getA(*tns, depth+1,beenthere);
if(remoteIP.empty()) {
cout<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl;
continue;
result=r.result(aabit);
cout<<prefix<<qname<<": Got "<<result.size()<<" answers from "<<*tns<<" ("<<remoteIP<<"), rcode="<<r.d_rcode<<endl;
-
cache_t tcache;
// reap all answers from this packet that are acceptable
for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
DNSResourceRecord rr=*i;
rr.d_place=DNSResourceRecord::ANSWER;
rr.ttl+=time(0);
+ // rr.ttl=time(0)+10+10*rr.qtype.getCode();
tcache[toLower(i->qname)+"|"+i->qtype.getName()].insert(rr);
}
else
// supplant
for(cache_t::const_iterator i=tcache.begin();i!=tcache.end();++i)
- cache[i->first]=i->second;
+ replaceCache(i->first,i->second);
set<string> nsset;
cout<<prefix<<qname<<": determining status after receiving this packet"<<endl;
else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) {
cout<<prefix<<qname<<": got a CNAME referral, starting over with "<<i->content<<endl<<endl;
ret.push_back(*i);
- return doResolve(i->content, qtype, ret,0);
+ set<GetBestNSAnswer>beenthere2;
+ return doResolve(i->content, qtype, ret,0,beenthere2);
}
// for ANY answers we *must* have an authoritive answer
else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && (i->qtype==qtype || ( qtype==QType(QType::ANY) && aabit))) {
cout<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl;
realreferral=true;
}
- else {
- cout<<prefix<<qname<<": got upwards NS record '"<<i->qname<<"' -> '"<<i->content<<"', already had '"<<auth<<"'"<<endl;
- }
+ else
+ cout<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl;
nsset.insert(toLower(i->content));
}
}
break;
}
else {
- cout<<prefix<<qname<<": status=NS "<<*tns<<" is lame for '"<<auth<<"', trying sibbling NS"<<endl;
+ cout<<prefix<<qname<<": status=NS "<<*tns<<" is lame for '"<<auth<<"', trying sibling NS"<<endl;
}
}
}
if((k->d_place==DNSResourceRecord::ANSWER && k->qtype==QType(QType::MX)) ||
(k->d_place==DNSResourceRecord::AUTHORITY && k->qtype==QType(QType::NS))) {
cout<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs an IP address"<<endl;
- doResolve(k->content,QType(QType::A),addit,1);
+ set<GetBestNSAnswer>beenthere;
+ doResolve(k->content,QType(QType::A),addit,1,beenthere);
}
for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) {