]> granicus.if.org Git - pdns/commitdiff
Welcome to the Mother of All DNS Parsers. This is the future of PowerDNS
authorBert Hubert <bert.hubert@netherlabs.nl>
Thu, 10 Feb 2005 16:53:25 +0000 (16:53 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Thu, 10 Feb 2005 16:53:25 +0000 (16:53 +0000)
packet generation and parsing. The actual merge will only be aroung the 3.0.5 timeframe
or so, but the code is there for 'sdig', which will be used in the resurrected
regression test system.

Also added --no-shuffle flag which is good for the regression test.

git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@303 d19b8d6e-7fed-0310-83ef-9ca221ded41b

pdns/Makefile.am
pdns/common_startup.cc
pdns/dnspacket.cc
pdns/dnsparser.cc [new file with mode: 0644]
pdns/dnsparser.hh [new file with mode: 0644]
pdns/dnsrecords.cc [new file with mode: 0644]
pdns/sdig.cc [new file with mode: 0644]

index a116aa97f25f84aa73cfc4e6ca7af1c8d1406e12..df1c9305d4f03b3510f2f1c557276d28b1af8c68 100644 (file)
@@ -13,10 +13,10 @@ sysconf_DATA = pdns.conf-dist
 if RECURSOR
 sbin_PROGRAMS = pdns_server pdns_recursor
 else
-sbin_PROGRAMS = pdns_server
+sbin_PROGRAMS = pdns_server 
 endif
 
-bin_PROGRAMS = pdns_control 
+bin_PROGRAMS = pdns_control sdig 
 
 EXTRA_PROGRAMS=pdns_recursor pdns_control qgen
 
@@ -41,6 +41,8 @@ sillyrecords.cc backends/bind/bindbackend.cc
 pdns_server_LDFLAGS= @moduleobjects@ @modulelibs@ @DYNLINKFLAGS@ @LIBDL@ @THREADFLAGS@
 pdns_server_INCLUDES=
 
+sdig_SOURCES=sdig.cc sstuff.hh dnsparser.cc dnsparser.hh dnsrecords.cc
+
 
 qgen_SOURCES=resolver.cc resolver.hh misc.cc unix_utility.cc qtype.cc \
        logger.cc statbag.cc dnspacket.cc arguments.cc  qgen.cc \
index 3f2f979d83a19ff4cec39848df43b49026007e70..204007aa35c52b8c4fdbd4013b0287c94200c347 100644 (file)
@@ -1,6 +1,6 @@
 /*
     PowerDNS Versatile Database Driven Nameserver
-    Copyright (C) 2002  PowerDNS.COM BV
+    Copyright (C) 2005  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 as published by
@@ -104,6 +104,7 @@ void declareArguments()
   arg().set("soa-minimum-ttl","Default SOA mininum ttl")="3600";
   arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600";
   arg().set("max-tcp-connections","Maximum number of TCP connections")="10";
+  arg().setSwitch("no-shuffle","Set this to prevent random shuffling of answers - for regression testing")="off";
 
   arg().setSwitch( "use-logfile", "Use a log file" )= "no";
   arg().set( "logfile", "Logfile to use" )= "pdns.log";
index b1cd1b5c6dcc0b5bfec81820ab9c3c7ebbdca45a..ba9754d1fa2d17f86a7605313e0230cea3f16576 100644 (file)
@@ -1,6 +1,6 @@
 /*
     PowerDNS Versatile Database Driven Nameserver
-    Copyright (C) 2002  PowerDNS.COM BV
+    Copyright (C) 2005  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 as published by
@@ -16,7 +16,7 @@
     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.32 2005/01/11 19:59:00 ahu Exp $
+
 #include "utility.hh"
 #include <cstdio>
 
@@ -970,29 +970,30 @@ void DNSPacket::wrapup(void)
 
   stable_sort(rrs.begin(),rrs.end(),rrcomp);
 
-  // now shuffle! start out with the ANSWER records  
-  vector<DNSResourceRecord>::iterator first, second;
-  for(first=rrs.begin();first!=rrs.end();++first) 
-    if(first->d_place==DNSResourceRecord::ANSWER && first->qtype.getCode() != QType::CNAME) // CNAME must come first
-      break;
-  for(second=first;second!=rrs.end();++second)
-    if(second->d_place!=DNSResourceRecord::ANSWER)
-      break;
-
-  if(second-first>1)
-    random_shuffle(first,second);
-
-  // now shuffle the additional records
-  for(first=second;first!=rrs.end();++first) 
-    if(first->d_place==DNSResourceRecord::ADDITIONAL && first->qtype.getCode() != QType::CNAME) // CNAME must come first
-      break;
-  for(second=first;second!=rrs.end();++second)
-    if(second->d_place!=DNSResourceRecord::ADDITIONAL)
-      break;
-
-  if(second-first>1)
-    random_shuffle(first,second);
-
+  if(!arg().mustDo("no-shuffle")) {
+    // now shuffle! start out with the ANSWER records  
+    vector<DNSResourceRecord>::iterator first, second;
+    for(first=rrs.begin();first!=rrs.end();++first) 
+      if(first->d_place==DNSResourceRecord::ANSWER && first->qtype.getCode() != QType::CNAME) // CNAME must come first
+       break;
+    for(second=first;second!=rrs.end();++second)
+      if(second->d_place!=DNSResourceRecord::ANSWER)
+       break;
+    
+    if(second-first>1)
+      random_shuffle(first,second);
+    
+    // now shuffle the additional records
+    for(first=second;first!=rrs.end();++first) 
+      if(first->d_place==DNSResourceRecord::ADDITIONAL && first->qtype.getCode() != QType::CNAME) // CNAME must come first
+       break;
+    for(second=first;second!=rrs.end();++second)
+      if(second->d_place!=DNSResourceRecord::ADDITIONAL)
+       break;
+    
+    if(second-first>1)
+      random_shuffle(first,second);
+  }
   d_wrapped=true;
 
 
diff --git a/pdns/dnsparser.cc b/pdns/dnsparser.cc
new file mode 100644 (file)
index 0000000..4e5100e
--- /dev/null
@@ -0,0 +1,234 @@
+#include "dnsparser.hh"
+#include <boost/lexical_cast.hpp>
+
+using namespace boost;
+
+class UnknownRecordContent : public DNSRecordContent
+{
+public:
+  UnknownRecordContent(const struct dnsrecordheader& ah, PacketReader& pr) 
+    : d_ah(ah)
+  {
+    pr.copyRecord(d_record, ah.d_clen);
+  }
+
+  string getType() const
+  {
+    return "#"+lexical_cast<string>(d_ah.d_type);
+  }
+
+
+
+  string getZoneRepresentation() const
+  {
+    ostringstream str;
+    if(d_ah.d_class==1)
+      str<<"IN";
+    else
+      str<<"CLASS"<<d_ah.d_class;
+
+    str<<"\t";
+
+    str<<"TYPE"<<d_ah.d_type<<"\t";
+
+    str<<"\\# "<<d_record.size()<<" ";
+    char hex[4];
+    for(size_t n=0; n<d_record.size(); ++n) {
+      snprintf(hex,sizeof(hex)-1, "%02x", d_record.at(n));
+      str << hex;
+    }
+    str<<"\n";
+    return str.str();
+  }
+  
+
+private:
+  struct dnsrecordheader d_ah;
+  vector<u_int8_t> d_record;
+};
+
+
+
+DNSRecordContent* DNSRecordContent::mastermake(const struct dnsrecordheader& ah, 
+                                              PacketReader& pr)
+{
+  typemap_t::const_iterator i=typemap.find(make_pair(ah.d_class, ah.d_type));
+  if(i==typemap.end()) {
+    return new UnknownRecordContent(ah, pr);
+  }
+  return i->second(ah, pr);
+}
+
+
+DNSRecordContent::typemap_t DNSRecordContent::typemap __attribute__((init_priority(1000)));
+
+void MOADNSParser::init(const char *packet, unsigned int len)
+{
+  if(len < sizeof(dnsheader))
+    throw MOADNSException("Packet shorter than minimal header");
+  
+  memcpy(&d_header, packet, sizeof(dnsheader));
+
+  d_header.qdcount=ntohs(d_header.qdcount);
+  d_header.ancount=ntohs(d_header.ancount);
+  d_header.nscount=ntohs(d_header.nscount);
+  d_header.arcount=ntohs(d_header.arcount);
+  
+  u_int16_t contentlen=len-sizeof(dnsheader);
+
+  d_content.resize(contentlen);
+  copy(packet+sizeof(dnsheader), packet+len, d_content.begin());
+  
+  unsigned int n;
+
+  PacketReader pr(d_content);
+
+  for(n=0;n < d_header.qdcount; ++n) {
+    d_qname=pr.getLabel();
+    d_qtype=pr.get16BitInt();
+    d_qclass=pr.get16BitInt();
+    //    cout<<"Question is for '"<<d_qname<<"', type "<<d_qtype<<endl;
+  }
+  
+  struct dnsrecordheader ah;
+  vector<unsigned char> record;
+  
+  for(n=0;n < d_header.ancount + d_header.nscount + d_header.arcount; ++n) {
+    DNSRecord dr;
+    
+    if(n < d_header.ancount)
+      dr.d_place=DNSRecord::Answer;
+    else if(n < d_header.ancount + d_header.nscount)
+      dr.d_place=DNSRecord::Nameserver;
+    else 
+      dr.d_place=DNSRecord::Additional;
+    
+    string label=pr.getLabel();
+    
+    pr.getDnsrecordheader(ah);
+    dr.d_ttl=ah.d_ttl;
+    dr.d_type=ah.d_type;
+    dr.d_class=ah.d_class;
+    
+    dr.d_label=label;
+    dr.d_ah=ah; 
+    d_answers.push_back(make_pair(dr, pr.d_pos));
+
+    dr.d_content=boost::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(ah, pr));
+    if(dr.d_content) {
+      //      cout<<dr.d_label<<"\t"<<dr.d_content->getZoneRepresentation();
+    }
+
+  }
+  
+  if(pr.d_pos!=contentlen) {
+    //    cout<<pr.d_pos<<" != " <<contentlen<<endl;
+    throw MOADNSException("Packet has trailing garbage");
+  }
+}
+
+void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
+{
+  unsigned int n;
+  unsigned char *p=reinterpret_cast<unsigned char*>(&ah);
+  
+  for(n=0; n < sizeof(dnsrecordheader); ++n) 
+    p[n]=d_content.at(d_pos++);
+  
+  ah.d_type=ntohs(ah.d_type);
+  ah.d_class=ntohs(ah.d_class);
+  ah.d_clen=ntohs(ah.d_clen);
+  ah.d_ttl=ntohl(ah.d_ttl);
+}
+
+
+void PacketReader::copyRecord(vector<unsigned char>& dest, u_int16_t len)
+{
+  dest.resize(len);
+  for(u_int16_t n=0;n<len;++n) {
+    dest.at(n)=d_content.at(d_pos++);
+  }
+}
+
+void PacketReader::copyRecord(unsigned char* dest, u_int16_t len)
+{
+  if(d_pos + len > d_content.size())
+    throw MOADNSException("Attempt to copy outside of packet");
+
+  memcpy(dest, &d_content[d_pos], len);
+  d_pos+=len;
+}
+
+
+u_int32_t PacketReader::get32BitInt()
+{
+  u_int32_t ret=0;
+  ret+=d_content.at(d_pos++);
+  ret<<=8;
+  ret+=d_content.at(d_pos++);
+  ret<<=8;
+  ret+=d_content.at(d_pos++);
+  ret<<=8;
+  ret+=d_content.at(d_pos++);
+  
+  return ret;
+}
+
+
+u_int16_t PacketReader::get16BitInt()
+{
+  return get16BitInt(d_content, d_pos);
+}
+
+u_int16_t PacketReader::get16BitInt(const vector<unsigned char>&content, u_int16_t& pos)
+{
+  u_int16_t ret=0;
+  ret+=content.at(pos++);
+  ret<<=8;
+  ret+=content.at(pos++);
+  
+  return ret;
+}
+
+u_int8_t PacketReader::get8BitInt()
+{
+  return d_content.at(d_pos++);
+}
+
+
+string PacketReader::getLabel(unsigned int recurs)
+{
+  return getLabelFromContent(d_content, d_pos, recurs++);
+}
+
+
+string PacketReader::getLabelFromContent(const vector<u_int8_t>& content, u_int16_t& frompos, int recurs) 
+{
+  if(recurs > 10)
+    throw MOADNSException("Loop");
+  
+  string ret;
+  for(;;) {
+    unsigned char labellen=content.at(frompos++);
+    
+    // cout<<"Labellen: "<<(int)labellen<<endl;
+    if(!labellen) {
+      if(ret.empty())
+       ret+=".";
+      break;
+    }
+    if((labellen & 0xc0) == 0xc0) {
+      u_int16_t offset=(labellen & ~0xc0) + content.at(frompos++) - sizeof(dnsheader);
+      //       cout<<"This is an offset, need to go to: "<<offset<<endl;
+      return ret+getLabelFromContent(content, offset, ++recurs);
+    }
+    else {
+      for(int n=0;n<labellen;++n) {
+       ret+=content.at(frompos++);
+      }
+      ret+=".";
+    }
+  }
+  
+  return ret;
+}
diff --git a/pdns/dnsparser.hh b/pdns/dnsparser.hh
new file mode 100644 (file)
index 0000000..0beea76
--- /dev/null
@@ -0,0 +1,118 @@
+#ifndef DNSPARSER_HH
+#define DNSPARSER_HH
+
+#include <map>
+#include <sstream>
+#include <stdexcept>
+#include <pcap.h>
+#include <iostream>
+#include <vector>
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <boost/shared_ptr.hpp>
+
+namespace {
+  typedef HEADER dnsheader;
+}
+
+using namespace std;
+using namespace boost;
+typedef runtime_error MOADNSException;
+
+struct dnsrecordheader
+{
+  u_int16_t d_type;
+  u_int16_t d_class;
+  u_int32_t d_ttl;
+  u_int16_t d_clen;
+} __attribute__((packed));
+
+
+class MOADNSParser;
+
+class PacketReader
+{
+public:
+  PacketReader(const vector<u_int8_t>& content) 
+    : d_pos(0), d_content(content)
+  {}
+
+  u_int32_t get32BitInt();
+  u_int16_t get16BitInt();
+  static u_int16_t get16BitInt(const vector<unsigned char>&content, u_int16_t& pos);
+  static string getLabelFromContent(const vector<u_int8_t>& content, u_int16_t& frompos, int recurs);
+  u_int8_t get8BitInt();
+  void getDnsrecordheader(struct dnsrecordheader &ah);
+  void copyRecord(vector<unsigned char>& dest, u_int16_t len);
+  void copyRecord(unsigned char* dest, u_int16_t len);
+  string getLabel(unsigned int recurs=0);
+
+  u_int16_t d_pos;
+private:
+  const vector<u_int8_t>& d_content;
+
+};
+
+class DNSRecordContent
+{
+public:
+  static DNSRecordContent* mastermake(const struct dnsrecordheader& ah, PacketReader& pr);
+  virtual std::string getZoneRepresentation() const = 0;
+  virtual std::string getType() const=0;
+  virtual ~DNSRecordContent() {}
+
+  std::string label;
+  struct dnsrecordheader header;
+protected:
+  typedef DNSRecordContent* makerfunc_t(const struct dnsrecordheader& ah, PacketReader& pr);
+  typedef std::map<std::pair<u_int16_t, u_int16_t>, makerfunc_t* > typemap_t;
+  static typemap_t typemap;
+};
+
+struct DNSRecord
+{
+  std::string d_label;
+  u_int16_t d_type;
+  u_int16_t d_class;
+  u_int32_t d_ttl;
+  enum {Answer, Nameserver, Additional} d_place;
+  boost::shared_ptr<DNSRecordContent> d_content;
+  struct dnsrecordheader d_ah;
+};
+
+class MOADNSParser
+{
+public:
+  MOADNSParser(const string& buffer) 
+  {
+    init(buffer.c_str(), buffer.size());
+  }
+
+  MOADNSParser(const char *packet, unsigned int len)
+  {
+    init(packet, len);
+  }
+  dnsheader d_header;
+  string d_qname;
+  u_int16_t d_qclass, d_qtype;
+
+  typedef vector<pair<DNSRecord, uint16_t > > answers_t;
+  answers_t d_answers;
+
+  shared_ptr<PacketReader> getPacketReader(uint16_t offset)
+  {
+    shared_ptr<PacketReader> pr(new PacketReader(d_content));
+    pr->d_pos=offset;
+    return pr;
+  }
+private:
+  void getDnsrecordheader(struct dnsrecordheader &ah);
+  void init(const char *packet, unsigned int len);
+  vector<uint8_t> d_content;
+};
+
+
+
+
+#endif
diff --git a/pdns/dnsrecords.cc b/pdns/dnsrecords.cc
new file mode 100644 (file)
index 0000000..dca3114
--- /dev/null
@@ -0,0 +1,268 @@
+#include "dnsparser.hh"
+#include <boost/lexical_cast.hpp>
+using namespace std;
+using namespace boost;
+
+
+class ARecordContent : public DNSRecordContent
+{
+public:
+  static void report(void)
+  {
+    typemap[make_pair(1,1)]=&make;
+  }
+
+  static DNSRecordContent* make(const struct dnsrecordheader& ah, PacketReader& pr) 
+  {
+    if(ah.d_clen!=4)
+      throw MOADNSException("Wrong size for A record");
+
+    ARecordContent* ret=new ARecordContent();
+    pr.copyRecord((unsigned char*) &ret->d_ip, 4);
+    return ret;
+  }
+
+  u_int32_t getIP() const
+  {
+    return d_ip;
+  }
+  
+  string getType() const
+  {
+    return "A";
+  }
+
+  string getZoneRepresentation() const
+  {
+    ostringstream str;
+    u_int32_t ip=ntohl(d_ip);
+
+    str<< ((ip >> 24)&0xff) << ".";
+    str<< ((ip >> 16)&0xff) << ".";
+    str<< ((ip >>  8)&0xff) << ".";
+    str<< ((ip      )&0xff);
+    return str.str();
+  }
+
+private:
+  u_int32_t d_ip;
+};
+
+class AAAARecordContent : public DNSRecordContent
+{
+public:
+  static void report(void)
+  {
+    typemap[make_pair(1,ns_t_aaaa)]=&make;
+  }
+
+  string getType() const
+  {
+    return "AAAA";
+  }
+
+
+  static DNSRecordContent* make(const struct dnsrecordheader& ah, PacketReader& pr) 
+  {
+    if(ah.d_clen!=16)
+      throw MOADNSException("Wrong size for AAAA record");
+
+    AAAARecordContent* ret=new AAAARecordContent();
+    pr.copyRecord((unsigned char*) &ret->d_ip6, 16);
+    return ret;
+  }
+  
+  string getZoneRepresentation() const
+  {
+    ostringstream str;
+
+    char hex[4];
+    for(size_t n=0; n< 16 ; n+=2) {
+      snprintf(hex,sizeof(hex)-1, "%x", d_ip6[n]);
+      str << hex;
+      snprintf(hex,sizeof(hex)-1, "%02x", d_ip6[n+1]);
+      str << hex;
+      if(n!=14)
+       str<<":";
+    }
+
+    return str.str();
+  }
+
+private:
+  unsigned char d_ip6[16];
+};
+
+
+namespace {
+  struct soatimes 
+  {
+    u_int32_t serial;
+    u_int32_t refresh;
+    u_int32_t retry;
+    u_int32_t expire;
+    u_int32_t minimum;
+  };
+}
+
+
+class OneLabelRecordContent : public DNSRecordContent
+{
+public:
+
+  OneLabelRecordContent(const struct dnsrecordheader& ah, const string& nsname) : d_type(ah.d_type), d_nsname(nsname) {}
+
+  static void report(void)
+  {
+    typemap[make_pair(1,ns_t_ns)]=&make;
+    typemap[make_pair(1,ns_t_cname)]=&make;
+    typemap[make_pair(1,ns_t_ptr)]=&make;
+  }
+
+  static DNSRecordContent* make(const struct dnsrecordheader& ah, PacketReader &pr) 
+  {
+    return new OneLabelRecordContent(ah, pr.getLabel());
+  }
+
+  string getType() const 
+  {
+    if(d_type==ns_t_ns)
+      return "NS";
+    else if(d_type==ns_t_cname)
+      return "CNAME";
+    if(d_type==ns_t_ptr)
+      return "PTR";
+  }
+
+  string getZoneRepresentation() const
+  {
+    ostringstream str;
+   
+    /*
+    str<<"IN\t";
+
+    str<<"\t";
+    */
+
+    return str.str();
+  }
+
+private:
+  u_int16_t d_type;
+  string d_nsname;
+
+};
+
+class SOARecordContent : public DNSRecordContent
+{
+public:
+
+  SOARecordContent(const string& mname, const string& rname, const struct soatimes& st) 
+    : d_mname(mname), d_rname(rname)
+  {
+    d_st=st;
+  }
+
+  static void report(void)
+  {
+    typemap[make_pair(1,6)]=&make;
+  }
+
+  string getType() const
+  {
+    return "SOA";
+  }
+
+
+  static DNSRecordContent* make(const struct dnsrecordheader& ah, PacketReader& pr) 
+  {
+    u_int16_t nowpos(pr.d_pos);
+    string mname=pr.getLabel();
+    string rname=pr.getLabel();
+
+    u_int16_t left=ah.d_clen - (pr.d_pos-nowpos);
+
+    if(left!=sizeof(struct soatimes))
+      throw MOADNSException("SOA RDATA has wrong size: "+lexical_cast<string>(left)+ ", should be "+lexical_cast<string>(sizeof(struct soatimes)));
+
+    struct soatimes st;
+    pr.copyRecord((unsigned char*)&st, sizeof(struct soatimes));
+
+    st.serial=ntohl(st.serial);
+    st.refresh=ntohl(st.refresh);
+    st.retry=ntohl(st.retry);
+    st.expire=ntohl(st.expire);
+    st.minimum=ntohl(st.minimum);
+    
+    return new SOARecordContent(mname, rname, st);
+  }
+
+  string getZoneRepresentation() const
+  {
+    ostringstream str;
+
+    str<<d_mname<<" "<<d_rname<<" ";
+    str<<d_st.serial<<" " << d_st.refresh <<" " <<d_st.retry << " " << d_st.expire<< " "<<d_st.minimum;
+    return str.str();
+  }
+
+
+  
+private:
+  string d_mname;
+  string d_rname;
+  struct soatimes d_st;
+};
+
+class MXRecordContent : public DNSRecordContent
+{
+public:
+
+  MXRecordContent(u_int16_t preference, const string& mxname) 
+    : d_preference(preference), d_mxname(mxname)
+  {
+  }
+
+  string getType() const
+  {
+    return "MX";
+  }
+
+
+  static void report(void)
+  {
+    typemap[make_pair(1,ns_t_mx)]=&make;
+  }
+
+  static DNSRecordContent* make(const struct dnsrecordheader& ah, PacketReader& pr) 
+  {
+    u_int16_t preference=pr.get16BitInt();
+    string mxname=pr.getLabel();
+
+    return new MXRecordContent(preference, mxname);
+  }
+
+  string getZoneRepresentation() const
+  {
+    ostringstream str;
+    str<<d_preference<<" "<<d_mxname;
+    return str.str();
+  }
+
+private:
+  u_int16_t d_preference;
+  string d_mxname;
+};
+
+
+static struct Reporter
+{
+  Reporter()
+  {
+    ARecordContent::report();
+    AAAARecordContent::report();
+    OneLabelRecordContent::report();
+    SOARecordContent::report();
+    MXRecordContent::report();
+  }
+} reporter __attribute__((init_priority(65535)));
diff --git a/pdns/sdig.cc b/pdns/sdig.cc
new file mode 100644 (file)
index 0000000..cb75462
--- /dev/null
@@ -0,0 +1,77 @@
+#include "dnsparser.hh"
+#include "sstuff.hh"
+#include "misc.hh"
+
+/** Write only packet generator */
+class DNSPacketGenerator
+{
+public:
+  DNSPacketGenerator(const string& qname, uint16_t qtype); // there is always a question
+  const string getPacket();
+  dnsheader d_dnsheader;
+private:
+  string d_content;
+};
+
+const string EncodeDNSLabel(const string& input)
+{
+  typedef vector<string> parts_t;
+  parts_t parts;
+  stringtok(parts,input,".");
+
+  string ret;
+  for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
+    ret.append(1,(char)i->length());
+    ret.append(*i);
+  }
+  ret.append(1,(char)0);
+  return ret;
+    
+}
+
+DNSPacketGenerator::DNSPacketGenerator(const string& qname, uint16_t qtype)
+{
+  memset(&d_dnsheader, 0, sizeof(d_dnsheader));
+  d_dnsheader.id=random();
+  d_dnsheader.qdcount=htons(1);
+
+  d_content=EncodeDNSLabel(qname);
+  qtype=htons(qtype);
+  d_content.append((char*)&qtype, 2);
+  qtype=htons(1);
+  d_content.append((char*)&qtype,2);
+}
+
+const string DNSPacketGenerator::getPacket()
+{
+  return string((char*)&d_dnsheader, ((char*)&d_dnsheader)+sizeof(d_dnsheader))+d_content;
+}
+
+int main(int argc, char** argv)
+try
+{
+  DNSPacketGenerator dpg(argv[3], atoi(argv[4]));
+
+  Socket sock(InterNetwork, Datagram);
+  IPEndpoint dest(argv[1], atoi(argv[2]));
+  sock.sendTo(dpg.getPacket(), dest);
+  
+  string reply;
+  sock.recvFrom(reply, dest);
+
+  MOADNSParser mdp(reply);
+  cout<<"Reply to question for qname='"<<mdp.d_qname<<"', qtype="<<mdp.d_qtype<<endl;
+
+  for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {          
+    shared_ptr<PacketReader> pr=mdp.getPacketReader(i->second);
+    DNSRecordContent* drc=DNSRecordContent::mastermake(i->first.d_ah, *pr);
+    cout<<i->first.d_place<<"\t"<<i->first.d_label<<"\tIN\t"<<drc->getType()<<"\t"<<i->first.d_ttl<<"\t"<<drc->getZoneRepresentation()<<endl;
+  }
+
+
+}
+catch(exception &e)
+{
+  cerr<<"Fatal: "<<e.what()<<endl;
+}
+