/*
PowerDNS Versatile Database Driven Nameserver
- Copyright (C) 2001 - 2005 PowerDNS.COM BV
+ Copyright (C) 2001 - 2007 PowerDNS.COM BV
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
return stringbuffer;
}
-string DNSPacket::getLocal() const
-{
- struct sockaddr_in6 sa;
- int addrlen=sizeof(sa);
-
- getsockname(d_socket, (struct sockaddr *)&sa, (socklen_t *)&addrlen);
- return sockAddrToString((struct sockaddr_in*)&sa);
-}
-
-
string DNSPacket::getRemote() const
{
return remote.toString();
// lowercase(qdomain); (why was this?)
return p-begin;
-
}
/** copies the question into our class
d.rd=b;
}
-
void DNSPacket::setOpcode(uint16_t opcode)
{
d.opcode=opcode;
<<endl;
}
-
-
void DNSPacket::addAAAARecord(const string &name, unsigned char addr[16], uint32_t ttl,DNSResourceRecord::Place place)
{
string piece1;
d.ancount++;
}
-
void DNSPacket::addMXRecord(const DNSResourceRecord &rr)
{
addMXRecord(rr.qname, rr.content, rr.priority, rr.ttl);
/** A DNS answer packets needs to include the original question. This function allows you to
paste in a question */
-
void DNSPacket::pasteQ(const char *question, int length)
{
stringbuffer.replace(12, length, question, length); // bytes 12 & onward need to become *question
}
-
-vector<DNSResourceRecord> DNSPacket::getAnswers()
-{
- // XXX FIXME a lot of this code happily touches bytes beyond your packet!
-
- vector<DNSResourceRecord> rrs;
- if(!(d.ancount|d.arcount|d.nscount))
- return rrs;
-
- const unsigned char *answerp=(const unsigned char *)stringbuffer.c_str()+d_qlen+12;
- const unsigned char *end=(const unsigned char *)stringbuffer.c_str()+len;
-
- int numanswers=ntohs(d.ancount) + ntohs(d.nscount) + ntohs(d.arcount);
- int length;
- uint16_t pos=0;
- while(numanswers--) {
- string name;
- int offset=0;
- offset=expand(answerp,end,name);
-
- DNSResourceRecord rr;
- rr.qname=name;
- rr.qtype=answerp[offset]*256+answerp[offset+1];
- rr.ttl=answerp[offset+7]+256*(answerp[offset+6]+256*(answerp[offset+5]+256*answerp[offset+4]));
- rr.content="";
- length=256*(unsigned char)answerp[offset+8]+(unsigned char)answerp[offset+8+1];
-
- const unsigned char *datapos=answerp+offset+10;
-
- if(datapos+length > end)
- throw AhuException("Record extends beyond end of packet");
-
- string part;
- offset=0;
-
- ostringstream o;
- int ip;
- int weight;
- int port;
-
- switch(rr.qtype.getCode()) {
-
- case QType::SOA:
- part=""; offset+=expand(datapos+offset,end,part); rr.content=part; // mname
- part=""; offset+=expand(datapos+offset,end,part); rr.content+=" "+part; // hostmaster
-
- // explicitly copy the SOA values out of the packet to avoid
- // SPARC alignment issues.
-
- rr.content+=" ";rr.content+=uitoa(getLong( datapos+offset ));
- rr.content+=" ";rr.content+=uitoa(getLong( datapos+offset+4 ));
- rr.content+=" ";rr.content+=uitoa(getLong( datapos+offset+8 ));
- rr.content+=" ";rr.content+=uitoa(getLong( datapos+offset+12 ));
- rr.content+=" ";rr.content+=uitoa(getLong( datapos+offset+16 ));
-
- break;
-
- case QType::A:
- ip = getLong(datapos);
-
- o.clear();
- o<<((ip>>24)&0xff)<<".";
- o<<((ip>>16)&0xff)<<".";
- o<<((ip>>8)&0xff)<<".";
- o<<((ip>>0)&0xff);
-
- rr.content=o.str();
- break;
-
- case QType::MX:
- rr.priority=(datapos[0] << 8) + datapos[1];
- expand(datapos+2,end,rr.content);
-
- break;
-
- case QType::TXT:
- rr.content.assign((const char *)datapos+offset+1,(int)datapos[offset]);
- break;
-
- case QType::HINFO: // this code is way way way overdue for a redesign
- rr.content="\"";
- rr.content.append((const char *)datapos+offset+1,(int)datapos[offset]);
- rr.content+="\" \"";
- rr.content.append((const char *)datapos+offset+(int)datapos[offset]+2,
- (int)datapos[offset+(int)datapos[offset]+1]);
- rr.content+="\"";
- break;
-
-
- case QType::LOC:
- rr.content=parseLOC(reinterpret_cast<const unsigned char *>(datapos+offset),length);
- break;
-
-
- case QType::SRV: // rfc 2052
- // priority goes into mx-priority
- rr.priority=(datapos[0] << 8) + datapos[1];
- // rest glue together
- weight = (datapos[2] << 8) + datapos[3];
- port = (datapos[4] << 8) + datapos[5];
- expand(datapos+offset+6,end,part);
- rr.content.assign(itoa(weight));
- rr.content+=" "+itoa(port)+" "+part;
- break;
-
- case QType::NAPTR: // rfc 2915
- {
- const string quote="\"";
- const string space=" ";
-
- int order;
- int pref;
- string flags, services, regex, replacement, result;
-
- order=(datapos[0] << 8) + datapos[1];
-
- // "pref" should maybe be put into rr.priority, but that
- // might have unintended side effects that I cannot
- // evaluate at this time.
- //
- // Lorens Kockum 2004-10-12
-
- pref=(datapos[2] << 8) + datapos[3];
-
- // The following would be a good subject for a mini-
- // function with boundary checking, which could be
- // reused in a lot of places in this file (see FIXME
- // at beginning of function).
-
- offset = 4 ;
- flags.assign((const char *)datapos+offset+1,(int)datapos[offset]);
- offset+=flags.size()+1;
- services.assign((const char *)datapos+offset+1,(int)datapos[offset]);
- offset+=services.size()+1;
- regex.assign((const char *)datapos+offset+1,(int)datapos[offset]);
- offset+=regex.size()+1;
-
- expand(datapos+offset,end,replacement);
-
- if (!replacement.size()) replacement = "." ;
-
- rr.content = itoa(order) \
- + " " + itoa(pref) \
- + " " + quote + flags + quote \
- + " " + quote + services + quote \
- + " " + quote + regex + quote \
- + " " + replacement;
- }
- break;
-
- case QType::RP:
- offset+=expand(datapos+offset,end,rr.content);
- expand(datapos+offset,end,part);
- rr.content+=" "+part;
- break;
-
-
- case QType::CNAME:
- case QType::NS:
- case QType::PTR:
- case QType::MR:
- expand(datapos+offset,end,rr.content);
- break;
-
- case QType::AAAA:
- if(length!=16)
- throw AhuException("Wrong length AAAA record returned from remote");
- char tmp[128];
-#ifdef AF_INET6
- if(!Utility::inet_ntop(AF_INET6, (const char *)datapos, tmp, sizeof(tmp)-1))
-#endif
- throw AhuException("Unable to translate record of type AAAA in resolver");
-
- rr.content=tmp;
- break;
-
- default:
- 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;
- else if(pos<ntohs(d.ancount)+ntohs(d.nscount))
- rr.d_place=DNSResourceRecord::AUTHORITY;
- else
- rr.d_place=DNSResourceRecord::ADDITIONAL;
-
- rrs.push_back(rr);
- pos++;
- // cout<<"Added '"<<rr.qname<<"' '"<<rr.content<<"' "<<rr.qtype.getName()<<endl;
- // cout<<"Advancing "<<length<<" bytes"<<endl;
- answerp=datapos+length;
- }
- return rrs;
-
-}
-
/** convenience function for creating a reply packet from a question packet. Do not forget to delete it after use! */
DNSPacket *DNSPacket::replyPacket() const
{
inline int parse(const char *mesg, int len); //!< parse a raw UDP or TCP packet and suck the data inward
string getString(); //!< for serialization - just passes the whole packet
- //! the raw DNS header
- struct dnsheader
- {
- unsigned int id:16; //!< id of this query/response
-#ifdef WORDS_BIGENDIAN // ultrasparc
- unsigned int qr:1; //!< 1 if this is a query, 0 if response
- unsigned int opcode:4; //!< the opcode
- unsigned int aa:1; //!< packet contains authoritative data
- unsigned int tc:1; //!< packet is truncated
- unsigned int rd:1; //!< this packets wants us to recurse
- unsigned int ra:1; //!< ??
- unsigned int unused:1; //!<
- unsigned int ad:1; //!< authentic data
- unsigned int cd:1; //!< checking disabled by resolver
- unsigned int rcode:4; //!< ??
-#else
- unsigned int rd:1; //!< this packets wants us to recurse
- unsigned int tc:1; //!< packet is truncated
- unsigned int aa:1; //!< packet contains authoritative data
- unsigned int opcode:4; //!< the opcode
- unsigned int qr:1; //!< 1 if this is a query, 0 if response
-
- ///////////
-
- unsigned int rcode:4; //!< ??
- unsigned int cd:1; //!< checking disabled by resolver
- unsigned int ad:1; //!< authentic data
- unsigned int unused:1; //!<
- unsigned int ra:1; //!< recursion available
-#endif
- ////////////////
-
- unsigned int qdcount:16; //!< number of questions
- unsigned int ancount:16; //!< number of answers
- unsigned int nscount:16; //!< number of authoritative nameservers included in answer
- unsigned int arcount:16; //!< number of additional resource records
- };
-
// address & socket manipulation
inline void setRemote(const ComboAddress*);
- string getLocal() const;
string getRemote() const;
+ string getLocal() const
+ {
+ ComboAddress ca;
+ socklen_t len=sizeof(ca);
+ getsockname(d_socket, (sockaddr*)&ca, &len);
+ return ca.toString();
+ }
uint16_t getRemotePort() const;
Utility::sock_t getSocket() const
void addRecord(const DNSResourceRecord &); // adds to 'rrs'
void setQuestion(int op, const string &qdomain, int qtype); // wipes 'd', sets a random id, creates start of packet (label, type, class etc)
- vector<DNSResourceRecord> getAnswers(); // this can be called only when a packet has been parsed
DTime d_dt; //!< the time this packet was created. replyPacket() copies this in for you, so d_dt becomes the time spent processing the question+answer
void wrapup(void); // writes out queued rrs, and generates the binary packet. also shuffles. also rectifies dnsheader 'd', and copies it to the stringbuffer
}
(*d_resanswers)++;
(*d_udpanswers)++;
- DNSPacket::dnsheader d;
+ dnsheader d;
memcpy(&d,buffer,sizeof(d));
{
Lock l(&d_lock);
#include "misc.hh"
#include "dnsparser.hh"
-DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const string& qname, uint16_t qtype, uint16_t qclass)
+DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const string& qname, uint16_t qtype, uint16_t qclass, uint8_t opcode)
: d_pos(0), d_content(content), d_qname(qname), d_qtype(qtype), d_qclass(qclass)
{
d_content.clear();
memset(&dnsheader, 0, sizeof(dnsheader));
dnsheader.id=0;
dnsheader.qdcount=htons(1);
+ dnsheader.opcode=opcode;
const uint8_t* ptr=(const uint8_t*)&dnsheader;
uint32_t len=d_content.size();
return (dnsheader*)&*d_content.begin();
}
-
void DNSPacketWriter::startRecord(const string& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, Place place)
{
if(!d_record.empty())
enum Place {ANSWER=1, AUTHORITY=2, ADDITIONAL=3};
//! Start a DNS Packet in the vector passed, with question qname, qtype and qclass
- DNSPacketWriter(vector<uint8_t>& content, const string& qname, uint16_t qtype, uint16_t qclass=1);
+ DNSPacketWriter(vector<uint8_t>& content, const string& qname, uint16_t qtype, uint16_t qclass=1, uint8_t opcode=0);
/** Start a new DNS record within this packet for namq, qtype, ttl, class and in the requested place. Note that packets can only be written in natural order -
ANSWER, AUTHORITY, ADDITIONAL */
/*
PowerDNS Versatile Database Driven Nameserver
- Copyright (C) 2002 - 2005 PowerDNS.COM BV
+ Copyright (C) 2002 - 2007 PowerDNS.COM BV
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
#include <cstring>
#include <string>
#include <vector>
-#include "dnspacket.hh"
+
#include "dns.hh"
#include "qtype.hh"
#include "tcpreceiver.hh"
#include "ahuexception.hh"
#include "statbag.hh"
#include "arguments.hh"
+#include "dnswriter.hh"
+#include "dnsparser.hh"
+#include <boost/shared_ptr.hpp>
+
+using namespace boost;
void Resolver::makeUDPSocket()
{
int Resolver::notify(int sock, const string &domain, const string &ip, uint16_t id)
{
- DNSPacket p;
- p.setQuestion(Opcode::Notify,domain,QType::SOA);
- p.wrapup();
- p.spoofID(id);
-
- struct in_addr inp;
- if(!Utility::inet_aton(ip.c_str(),&inp))
- throw ResolverException("Unable to convert '"+ip+"' to an IP address");
-
- struct sockaddr_in toaddr;
- toaddr.sin_addr.s_addr=inp.s_addr;
-
- toaddr.sin_port=htons(53);
- toaddr.sin_family=AF_INET;
+ vector<uint8_t> packet;
+ DNSPacketWriter pw(packet, domain, QType::SOA, Opcode::Notify);
+ pw.getHeader()->id = id;
+
+ ComboAddress dest(ip, 53);
- if(sendto(sock, p.getData(), p.len, 0, (struct sockaddr*)(&toaddr), sizeof(toaddr))<0) {
+ if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&dest), dest.getSocklen())<0) {
throw ResolverException("Unable to send notify to "+ip+": "+stringerror());
}
return true;
void Resolver::sendResolve(const string &ip, const char *domain, int type)
{
- DNSPacket p;
-
- p.setQuestion(Opcode::Query,domain,type);
- p.wrapup();
+ vector<uint8_t> packet;
+ DNSPacketWriter pw(packet, domain, type);
d_domain=domain;
d_type=type;
d_inaxfr=false;
- struct sockaddr_in toaddr;
- struct in_addr inp;
ServiceTuple st;
st.port=53;
try {
throw ResolverException("Sending a dns question to '"+ip+"': "+ae.reason);
}
- Utility::inet_aton(st.host.c_str(),&inp);
- toaddr.sin_addr.s_addr=inp.s_addr;
-
- toaddr.sin_port=htons(st.port);
- toaddr.sin_family=AF_INET;
+ ComboAddress remote(st.host, st.port);
- if(sendto(d_sock, p.getData(), p.len, 0, (struct sockaddr*)(&toaddr), sizeof(toaddr))<0) {
+ if(sendto(d_sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen()) < 0) {
throw ResolverException("Unable to ask query of "+st.host+":"+itoa(st.port)+": "+stringerror());
}
}
if(d_sock>=0)
return;
- struct in_addr inp;
- Utility::inet_aton(ip.c_str(),&inp);
- d_toaddr.sin4.sin_addr.s_addr=inp.s_addr;
-
- d_toaddr.sin4.sin_port=htons(port);
- d_toaddr.sin4.sin_family=AF_INET;
+ d_toaddr=ComboAddress(ip, port);
d_sock=socket(AF_INET,SOCK_STREAM,0);
if(d_sock<0)
// Use query-local-address as source IP for queries, if specified.
string querylocaladdress(arg()["query-local-address"]);
- if (querylocaladdress!="") {
- struct sockaddr_in fromaddr;
- struct hostent *h=0;
+ if (!querylocaladdress.empty()) {
+ ComboAddress fromaddr(querylocaladdress, 0);
- h = gethostbyname(querylocaladdress.c_str());
- if(!h) {
- Utility::closesocket(d_sock);
- d_sock=-1;
- throw ResolverException("Unable to resolve query local address");
- }
-
- fromaddr.sin_family = AF_INET;
- fromaddr.sin_addr.s_addr = *(int*)h->h_addr;
- fromaddr.sin_port = 0;
-
- if (bind(d_sock, (struct sockaddr *)&fromaddr, sizeof(fromaddr)) < 0) {
+ if (bind(d_sock, (struct sockaddr *)&fromaddr, fromaddr.getSocklen()) < 0) {
Utility::closesocket(d_sock);
d_sock=-1;
throw ResolverException("Binding to query-local-address: "+stringerror());
// d_sock now connected
}
-
//! returns -1 for permanent error, 0 for timeout, 1 for success
int Resolver::axfr(const string &ip, const char *domain)
{
makeTCPSocket(ip);
d_type=QType::AXFR;
- DNSPacket p;
- p.d_tcp = true;
- p.setQuestion(Opcode::Query,domain,QType::AXFR);
- p.wrapup();
+
+ vector<uint8_t> packet;
+ DNSPacketWriter pw(packet, domain, QType::AXFR);
- uint16_t replen=htons(p.len);
+ uint16_t replen=htons(packet.size());
Utility::iovec iov[2];
iov[0].iov_base=(char*)&replen;
iov[0].iov_len=2;
- iov[1].iov_base=(char*)p.getData();
- iov[1].iov_len=p.len;
+ iov[1].iov_base=(char*)&packet[0];
+ iov[1].iov_len=packet.size();
int ret=Utility::writev(d_sock,iov,2);
if(ret<0)
Resolver::res_t Resolver::result()
{
+ shared_ptr<MOADNSParser> mdp;
+
try {
- DNSPacket p;
- p.setRemote(&d_toaddr);
- p.d_tcp = d_inaxfr; // fixes debian bug 330184
- if(p.parse((char *)d_buf, d_len)<0)
- throw ResolverException("resolver: unable to parse packet of "+itoa(d_len)+" bytes");
-
- if(p.d.rcode)
- if(d_inaxfr)
- throw ResolverException("Remote nameserver unable/unwilling to AXFR with us: RCODE="+itoa(p.d.rcode));
- else
- throw ResolverException("Remote nameserver reported error: RCODE="+itoa(p.d.rcode));
-
+ mdp=shared_ptr<MOADNSParser>(new MOADNSParser((char*)d_buf, d_len));
+ }
+ catch(...) {
+ throw ResolverException("resolver: unable to parse packet of "+itoa(d_len)+" bytes");
+ }
+ if(mdp->d_header.rcode)
+ if(d_inaxfr)
+ throw ResolverException("Remote nameserver unable/unwilling to AXFR with us: RCODE="+itoa(mdp->d_header.rcode));
+ else
+ throw ResolverException("Remote nameserver reported error: RCODE="+itoa(mdp->d_header.rcode));
+
if(!d_inaxfr) {
- if(ntohs(p.d.qdcount)!=1)
- throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(ntohs(p.d.qdcount))+")");
+ if(mdp->d_header.qdcount!=1)
+ throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp->d_header.qdcount)+")");
- if(p.qdomain!=d_domain)
- throw ResolverException(string("resolver: received an answer to another question (")+p.qdomain+"!="+d_domain+")");
+ if(mdp->d_qname != d_domain+".")
+ throw ResolverException(string("resolver: received an answer to another question (")+mdp->d_qname+"!="+d_domain+".)");
}
- return p.getAnswers();
- }
- catch(AhuException &ae) { // translate
- throw ResolverException(ae.reason);
- }
+
+ vector<DNSResourceRecord> ret;
+ DNSResourceRecord rr;
+ for(MOADNSParser::answers_t::const_iterator i=mdp->d_answers.begin(); i!=mdp->d_answers.end(); ++i) {
+ rr.qname = i->first.d_label;
+ rr.qtype = i->first.d_type;
+ rr.ttl = i->first.d_ttl;
+ rr.content = i->first.d_content->getZoneRepresentation();
+ if(rr.qtype.getCode() == QType::MX) {
+ vector<string> parts;
+ stringtok(parts, rr.content);
+ rr.priority = atoi(parts[0].c_str());
+ if(parts.size() > 1)
+ rr.content=parts[1];
+ } else if(rr.qtype.getCode() == QType::SRV) {
+ rr.priority = atoi(rr.content.c_str());
+ vector<pair<string::size_type, string::size_type> > fields;
+ vstringtok(fields, rr.content, " ");
+ if(fields.size()==4)
+ rr.content=string(rr.content.c_str() + fields[1].first, fields[3].second - fields[1].first);
+ }
+ ret.push_back(rr);
+ }
+
+ return ret;
}
-
void Resolver::sendSoaSerialRequest(const string &ip, const string &domain)
{
sendResolve(ip,domain.c_str(),QType::SOA);