diff --git a/Makefile.in b/Makefile.in
index 4ba88c7..f76581e 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -102,7 +102,7 @@ export SRCS = charpool.cc FingerPrintResults.cc FPEngine.cc FPModel.cc idle_scan
 
 export HDRS = charpool.h FingerPrintResults.h FPEngine.h idle_scan.h MACLookup.h nmap_amigaos.h nmap_dns.h nmap_error.h nmap.h nmap_ftp.h NmapOps.h NmapOutputTable.h nmap_tty.h nmap_winconfig.h osscan2.h osscan.h output.h payload.h portlist.h portreasons.h protocols.h scan_engine.h scan_engine_connect.h scan_engine_raw.h service_scan.h services.h TargetGroup.h Target.h targets.h tcpip.h timing.h traceroute.h utils.h xml.h $(NSE_HDRS)
 
-OBJS = charpool.o FingerPrintResults.o FPEngine.o FPModel.o idle_scan.o MACLookup.o main.o nmap_dns.o nmap_error.o nmap.o nmap_ftp.o NmapOps.o NmapOutputTable.o nmap_tty.o osscan2.o osscan.o output.o payload.o portlist.o portreasons.o protocols.o scan_engine.o scan_engine_connect.o scan_engine_raw.o service_scan.o services.o TargetGroup.o Target.o targets.o tcpip.o timing.o traceroute.o utils.o xml.o $(NSE_OBJS)
+OBJS = charpool.o FingerPrintResults.o FPEngine.o FPModel.o idle_scan.o MACLookup.o nmap_dns.o nmap_error.o nmap.o nmap_ftp.o NmapOps.o NmapOutputTable.o nmap_tty.o osscan2.o osscan.o output.o payload.o portlist.o portreasons.o protocols.o scan_engine.o scan_engine_connect.o scan_engine_raw.o service_scan.o services.o TargetGroup.o Target.o targets.o tcpip.o timing.o traceroute.o utils.o xml.o $(NSE_OBJS)
 
 # %.o : %.cc -- nope this is a GNU extension
 .cc.o:
@@ -116,10 +116,10 @@ FPModel.o: CXXFLAGS += -g0
 all: @LUA_BUILD@ @LIBLINEAR_BUILD@ @PCAP_BUILD@ @PCRE_BUILD@ @DNET_BUILD@ @NBASE_BUILD@ @NSOCK_BUILD@ @NCAT_BUILD@ @NMAP_UPDATE_BUILD@ netutil_build
 	$(MAKE) $(TARGET) $(BUILDZENMAP) $(BUILDNDIFF) $(BUILDNPING)
 
-$(TARGET): @LUA_DEPENDS@ @LIBLINEAR_DEPENDS@ @PCAP_DEPENDS@ @PCRE_DEPENDS@ @DNET_DEPENDS@ $(NBASEDIR)/libnbase.a $(NSOCKDIR)/src/libnsock.a libnetutil/libnetutil.a $(OBJS)
+$(TARGET): @LUA_DEPENDS@ @LIBLINEAR_DEPENDS@ @PCAP_DEPENDS@ @PCRE_DEPENDS@ @DNET_DEPENDS@ $(NBASEDIR)/libnbase.a $(NSOCKDIR)/src/libnsock.a libnetutil/libnetutil.a $(OBJS) main.o
 	@echo Compiling nmap
 	rm -f $@
-	$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+	$(CXX) $(LDFLAGS) -o $@ $(OBJS) main.o $(LIBS)
 
 build-%: %/Makefile
 	cd $* && $(MAKE)
@@ -185,7 +185,7 @@ release-rpms:
 web:
 	cd $(NMAPDEVDIR) && $(MAKE) web
 
-clean: @LUA_CLEAN@ @LIBLINEAR_CLEAN@ @PCAP_CLEAN@ @PCRE_CLEAN@ @DNET_CLEAN@ nsock_clean nbase_clean netutil_clean my_clean @NPING_CLEAN@ @ZENMAP_CLEAN@ @NCAT_CLEAN@ @NMAP_UPDATE_CLEAN@ @NDIFF_CLEAN@
+clean: @LUA_CLEAN@ @LIBLINEAR_CLEAN@ @PCAP_CLEAN@ @PCRE_CLEAN@ @DNET_CLEAN@ nsock_clean nbase_clean netutil_clean my_clean @NPING_CLEAN@ @ZENMAP_CLEAN@ @NCAT_CLEAN@ @NMAP_UPDATE_CLEAN@ @NDIFF_CLEAN@ tests/clean
 
 my_clean:
 	rm -f dependencies.mk
@@ -297,6 +297,12 @@ nmap-update/default_channel.h: nmap.h
 	sed -e 's/^#[ \t]*define[ \t]\+DEFAULT_CHANNEL[ \t]\+\(".*"\)/#define DEFAULT_CHANNEL "$(NMAP_UPDATE_CHANNEL)"/' $@ > $@.tmp
 	mv -f $@.tmp $@
 
+tests/check_dns: $(OBJS)
+	 $(CXX) -o $@ $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $^ $(LIBS) tests/nmap_dns_test.cc
+
+tests/clean:
+	@rm -f tests/check_dns
+
 # By default distutils rewrites installed scripts to hardcode the
 # location of the Python interpreter they were built with (something
 # like #!/usr/bin/python2.4). This is the wrong thing to do when
@@ -408,7 +414,10 @@ nsock_check:
 zenmap_check:
 	@cd $(ZENMAPDIR)/test && $(PYTHON) run_tests.py
 
-check: @NCAT_CHECK@ @NSOCK_CHECK@ @ZENMAP_CHECK@ @NSE_CHECK@ @NDIFF_CHECK@
+dns_check: tests/check_dns
+	$<
+
+check: @NCAT_CHECK@ @NSOCK_CHECK@ @ZENMAP_CHECK@ @NSE_CHECK@ @NDIFF_CHECK@ dns_check
 
 ${srcdir}/configure: configure.ac 
 	cd ${srcdir} && autoconf
diff --git a/nbase/nbase.h b/nbase/nbase.h
index 2c1616b..a63475c 100644
--- a/nbase/nbase.h
+++ b/nbase/nbase.h
@@ -224,6 +224,7 @@
 #undef NDEBUG
 
 /* Integer types */
+#include <stdint.h>
 typedef uint8_t u8;
 typedef int8_t s8;
 typedef uint16_t u16;
diff --git a/nbase/nbase_ipv6.h b/nbase/nbase_ipv6.h
index 7fd16f6..7737b88 100644
--- a/nbase/nbase_ipv6.h
+++ b/nbase/nbase_ipv6.h
@@ -271,5 +271,7 @@ int getaddrinfo(const char *node, const char *service,
 const char *gai_strerror(int errcode);
 #endif
 
+int sockaddr_storage_inet_pton(const char * ip_str, struct sockaddr_storage * addr);
+const char *sockaddr_storage_iptop(const struct sockaddr_storage * addr, char * dst);
 
 #endif /* NBASE_IPV6_H */
diff --git a/nbase/nbase_misc.c b/nbase/nbase_misc.c
index cd53465..12ecc9e 100644
--- a/nbase/nbase_misc.c
+++ b/nbase/nbase_misc.c
@@ -910,3 +910,45 @@ char *executable_path(const char *argv0) {
 
   return path;
 }
+
+int sockaddr_storage_inet_pton(const char * ip_str, struct sockaddr_storage * addr)
+{
+#if HAVE_IPV6
+  struct sockaddr_in6 * addrv6p = (struct sockaddr_in6 *) addr;
+  if ( 1 == inet_pton(AF_INET6, ip_str, &(addrv6p->sin6_addr)) )
+  {
+    addr->ss_family = AF_INET6;
+    return 1;
+  }
+#endif // HAVE_IPV6
+
+  struct sockaddr_in * addrv4p = (struct sockaddr_in *) addr;
+  if ( 1 == inet_pton(AF_INET, ip_str, &(addrv4p->sin_addr)) )
+  {
+    addr->ss_family = AF_INET;
+    return 1;
+  }
+
+  return 0;
+}
+
+const char *sockaddr_storage_iptop(const struct sockaddr_storage * addr, char * dst)
+{
+  switch (addr->ss_family){
+  case AF_INET:
+  {
+    const struct sockaddr_in * ipv4_ptr = (const struct sockaddr_in *) addr;
+    return inet_ntop(addr->ss_family, &(ipv4_ptr->sin_addr), dst, INET_ADDRSTRLEN);
+  }
+#if HAVE_IPV6
+  case AF_INET6:
+  {
+    const struct sockaddr_in6 * addrv6p = (struct sockaddr_in6 *) addr;
+    return inet_ntop(addr->ss_family, &(addrv6p->sin6_addr), dst, INET6_ADDRSTRLEN);
+  }
+#endif
+  default:
+  {
+    return NULL;
+  }}
+}
diff --git a/nmap_dns.cc b/nmap_dns.cc
index e0c4ade..5e12c0a 100644
--- a/nmap_dns.cc
+++ b/nmap_dns.cc
@@ -162,13 +162,10 @@
 //     --see http://accs-net.com/hosts/how_to_use_hosts.html
 //
 //
-// Created by Doug Hoyte
-// doug at hcsw.org
-// http://www.hcsw.org
+// Created by Doug Hoyte <doug at hcsw.org> http://www.hcsw.org
+// DNS Caching and aging added by Eddie Bell ejlbell () gmail com 2007
+// IPv6 and improved DNS cache by Gioacchino Mazzurco <gmazzurco89 () gmail com> 2015
 
-/*
- * DNS Caching and aging added by Eddie Bell ejlbell () gmail com 2007
- */
 
 // TODO:
 //
@@ -194,7 +191,6 @@
 #include <limits.h>
 #include <list>
 #include <vector>
-#include <algorithm>
 
 extern NmapOps o;
 
@@ -261,18 +257,12 @@ static int read_timeouts[][4] = {
 // retransmission. This should almost never happen. (in milliseconds)
 #define WRITE_TIMEOUT 100
 
-// Size of hash table used to hold the hosts from /etc/hosts
-#define HASH_TABLE_SIZE 256
-
-// Hash macro for etchosts
-#define IP_HASH(x) (ntohl(x)%HASH_TABLE_SIZE)
-
 
 //------------------- Internal Structures ---------------------
 
 struct dns_server;
 struct request;
-struct host_elem;
+typedef struct sockaddr_storage sockaddr_storage;
 
 struct dns_server {
   std::string hostname;
@@ -297,15 +287,136 @@ struct request {
   u16 id;
 };
 
-struct host_elem {
-  std::string name;
-  u32 addr;
+class HostElem
+{
+public:
+  HostElem(const std::string & name_, const sockaddr_storage & ip) :
+    name(name_), addr(ip), cache_hits(0) {}
+  ~HostElem() {}
+
+  /* Ages entries and return true with a cache hit of 0 (the least used) */
+  static bool isTimeToClean(HostElem he)
+  {
+    if(he.cache_hits)
+    {
+      he.cache_hits >>= 1;
+      return false;
+    }
+
+    return true;
+  }
+
+  const std::string name;
+  const sockaddr_storage addr;
   u8 cache_hits;
 };
 
+class HostCacheLine : public std::list<HostElem>{};
+
+class HostCache
+{
+public:
+  //            TODO: avoid hardcode this constant
+  HostCache() : lines_count(256), hash_mask(lines_count-1),
+    hosts_storage(new HostCacheLine[lines_count]), elements_count(0)
+  {}
+  ~HostCache()
+  {
+    delete[] hosts_storage;
+  }
+
+  u32 hash(sockaddr_storage ip)
+  {
+    u32 ret = 0;
+
+    switch (ip.ss_family)
+    {
+      case AF_INET:
+      {
+        u8 * ipv4 = (u8 *) &((const struct sockaddr_in *) &ip)->sin_addr;
+        // Shuffle bytes a little so we avoid awful performances in commons
+        // usages patterns like 10.0.1-255.1 and lines_count 256
+        ret = ipv4[0] + (ipv4[1]<<3) + (ipv4[2]<<5) + (ipv4[3]<<7);
+        break;
+      }
+      case AF_INET6:
+      {
+        const struct sockaddr_in6 * sa6 = (const struct sockaddr_in6 *) &ip;
+        u32 * ipv6 = (u32 *) sa6->sin6_addr.s6_addr;
+        ret = ipv6[0] + ipv6[1] + ipv6[2] + ipv6[3];
+        break;
+      }
+    }
+
+    return ret & hash_mask;
+  }
+
+  /* Add to the dns cache. If there are too many entries
+   * we age and remove the least frequently used ones to
+   * make more space. */
+  bool add( const sockaddr_storage & ip, const std::string & hname)
+  {
+    std::string discard;
+    if(lookup(ip, discard)) return false;
+
+    if(elements_count >= lines_count) prune();
+
+    HostElem he(hname, ip);
+    hosts_storage[hash(ip)].push_back(he);
+    ++elements_count;
+    return true;
+  }
+
+  u32 prune()
+  {
+    u32 original_count = elements_count;
+    for(u32 i = 0; i < lines_count; ++i)
+    {
+      std::list<HostElem>::iterator it = find_if(hosts_storage[i].begin(),
+                                                 hosts_storage[i].end(),
+                                                 HostElem::isTimeToClean);
+      while ( it != hosts_storage[i].end() )
+      {
+        hosts_storage[i].erase(it);
+        assert(elements_count > 0);
+        --elements_count;
+      }
+    }
+
+    return original_count - elements_count;
+  }
+
+  /* Search for a hostname in the cache and increment
+   * its cache hit counter if found */
+  bool lookup(const sockaddr_storage & ip, std::string & name)
+  {
+    std::list<HostElem>::iterator hostI;
+    uint ip_hash = hash(ip);
+    for( hostI = hosts_storage[ip_hash].begin();
+         hostI != hosts_storage[ip_hash].end();
+         ++hostI)
+    {
+      if (sockaddr_storage_equal(&hostI->addr, &ip))
+      {
+        if(hostI->cache_hits < UCHAR_MAX)
+          hostI->cache_hits++;
+        name = hostI->name;
+        return true;
+      }
+    }
+    return false;
+  }
+
+protected:
+  const u32 lines_count;
+  const u32 hash_mask;
+  HostCacheLine * const hosts_storage;
+  u32 elements_count;
+};
 
 //------------------- Globals ---------------------
 
+u16 DNS::Factory::progressiveId = get_random_u16();
 static std::list<dns_server> servs;
 static std::list<request *> new_reqs;
 static std::list<request *> cname_reqs;
@@ -313,12 +424,11 @@ static int total_reqs;
 static nsock_pool dnspool=NULL;
 
 /* The DNS cache, not just for entries from /etc/hosts. */
-static std::list<host_elem> etchosts[HASH_TABLE_SIZE];
+static HostCache host_cache;
 
 static int stat_actual, stat_ok, stat_nx, stat_sf, stat_trans, stat_dropped, stat_cname;
 static struct timeval starttv;
 static int read_timeout_index;
-static u16 id_counter;
 
 static int firstrun=1;
 static ScanProgressMeter *SPM;
@@ -327,8 +437,6 @@ static ScanProgressMeter *SPM;
 //------------------- Prototypes and macros ---------------------
 
 static void put_dns_packet_on_wire(request *req);
-static const char *lookup_etchosts(u32 ip);
-static void addto_etchosts(u32 ip, const char *hname);
 
 #define ACTION_FINISHED 0
 #define ACTION_CNAME_LIST 1
@@ -370,20 +478,6 @@ static void close_dns_servers() {
 }
 
 
-// Inserts an integer (endian non-specifically) into a DNS packet.
-// Returns number of bytes written
-static int add_integer_to_dns_packet(char *packet, int c) {
-  char tpnum[4];
-  int tplen;
-
-  sprintf(tpnum, "%d", c);
-  tplen = strlen(tpnum);
-  packet[0] = (char) tplen;
-  memcpy(packet+1, tpnum, tplen);
-
-  return tplen+1;
-}
-
 // Puts as many packets on the line as capacity will allow
 static void do_possible_writes() {
   std::list<dns_server>::iterator servI;
@@ -425,26 +519,13 @@ static void write_evt_handler(nsock_pool nsp, nsock_event evt, void *req_v) {
 // (calls nsock_write()). Does various other tasks like recording
 // the time for the timeout.
 static void put_dns_packet_on_wire(request *req) {
-  char packet[512];
-  int plen=0;
-  u32 ip;
-  struct timeval now, timeout;
-
-  ip = (u32) ntohl(req->targ->v4host().s_addr);
-  packet[0] = (req->id >> 8) & 0xFF;
-  packet[1] = req->id & 0xFF;
-  plen += 2;
+  const size_t maxlen = 512;
+  u8 packet[maxlen];
+  size_t plen=0;
 
-  memcpy(packet+plen, "\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00", 10);
-  plen += 10;
-
-  plen += add_integer_to_dns_packet(packet+plen, ip & 0xFF);
-  plen += add_integer_to_dns_packet(packet+plen, (ip>>8) & 0xFF);
-  plen += add_integer_to_dns_packet(packet+plen, (ip>>16) & 0xFF);
-  plen += add_integer_to_dns_packet(packet+plen, (ip>>24) & 0xFF);
+  struct timeval now, timeout;
 
-  memcpy(packet+plen, "\x07in-addr\004arpa\x00\x00\x0c\x00\x01", 18);
-  plen += 18;
+  plen = DNS::Factory::buildReverseRequest(*req->targ->TargetSockAddr(), packet, maxlen);
 
   req->curr_server->write_busy = 1;
   req->curr_server->reqs_on_wire++;
@@ -455,7 +536,7 @@ static void put_dns_packet_on_wire(request *req) {
 
   req->tries++;
 
-  nsock_write(dnspool, req->curr_server->nsd, write_evt_handler, WRITE_TIMEOUT, req, packet, plen);
+  nsock_write(dnspool, req->curr_server->nsd, write_evt_handler, WRITE_TIMEOUT, req, reinterpret_cast<const char *>(packet), plen);
 }
 
 // Processes DNS packets that have timed out
@@ -542,37 +623,48 @@ static int deal_with_timedout_reads() {
 // After processing a DNS response, we search through the IPs we're
 // looking for and update their results as necessary.
 // Returns non-zero if this matches a query we're looking for
-static int process_result(u32 ia, char *result, int action, u16 id) {
+static int process_result(const sockaddr_storage &ip, const std::string &result, int action, u16 id)
+{
   std::list<dns_server>::iterator servI;
   std::list<request *>::iterator reqI;
   request *tpreq;
 
-  for(servI = servs.begin(); servI != servs.end(); servI++) {
-    for(reqI = servI->in_process.begin(); reqI != servI->in_process.end(); reqI++) {
+  for(servI = servs.begin(); servI != servs.end(); servI++)
+  {
+    /* TODO: This is higly inefficent we do on average
+     * (0.5 * servs.size() * in_process.size())
+     * iterations to find a a request, we should use a map
+     * to do this efficiently
+     */
+    for(reqI = servI->in_process.begin(); reqI != servI->in_process.end(); reqI++)
+    {
       tpreq = *reqI;
-
-      if (id == tpreq->id) {
-
-        if (ia != 0 && tpreq->targ->v4host().s_addr != ia)
+      if (id == tpreq->id)
+      {
+        if(!result.empty() && (!sockaddr_storage_equal(&ip, tpreq->targ->TargetSockAddr())))
           continue;
 
-        if (action == ACTION_CNAME_LIST || action == ACTION_FINISHED) {
-        servI->capacity += CAPACITY_UP_STEP;
-        check_capacities(&*servI);
+        if (action == ACTION_CNAME_LIST || action == ACTION_FINISHED)
+        {
+          servI->capacity += CAPACITY_UP_STEP;
+          check_capacities(&*servI);
 
-        if (result) {
-          tpreq->targ->setHostName(result);
-          addto_etchosts(tpreq->targ->v4hostip()->s_addr, result);
-        }
+          if(!result.empty())
+          {
+            tpreq->targ->setHostName(result.c_str());
+            host_cache.add(* tpreq->targ->TargetSockAddr(), result);
+          }
 
-        servI->in_process.remove(tpreq);
-        servI->reqs_on_wire--;
+          servI->in_process.remove(tpreq);
+          servI->reqs_on_wire--;
 
-        total_reqs--;
+          total_reqs--;
 
           if (action == ACTION_CNAME_LIST) cname_reqs.push_back(tpreq);
           if (action == ACTION_FINISHED) delete tpreq;
-        } else {
+        }
+        else
+        {
           memcpy(&tpreq->timeout, nsock_gettimeofday(), sizeof(struct timeval));
           deal_with_timedout_reads();
         }
@@ -591,115 +683,11 @@ static int process_result(u32 ia, char *result, int action, u16 id) {
   return 0;
 }
 
-
-// Gets an IP address from a X.X.X.X.in-addr.arpa DNS
-// encoded string inside a packet.
-// maxlen is the very maximum length (in total bytes)
-// that should be processed
-static u32 parse_inaddr_arpa(unsigned char *buf, int maxlen) {
-  u32 ip=0;
-  int i, j;
-
-  if (maxlen <= 0) return 0;
-
-  for (i=0; i<=3; i++) {
-    if (buf[0] < 1 || buf[0] > 3) return 0;
-
-    maxlen -= buf[0] + 1;
-    if (maxlen <= 0) return 0;
-
-    for (j=1; j<=buf[0]; j++) if (!isdigit((int) buf[j])) return 0;
-
-    ip |= atoi((char *) buf+1) << (8*i);
-    buf += buf[0] + 1;
-  }
-
-  if (maxlen < 14) return 0; // length of the following string
-  if (strcasecmp((char *) buf, "\x07in-addr\004arpa\0")) return 0;
-
-  return ntohl(ip);
-}
-
-
-// Turns a DNS packet encoded name (see the RFC) and turns it into
-// a normal decimal separated hostname.
-// ASSUMES NAME LENGTH/VALIDITY HAS ALREADY BEEN VERIFIED
-static int encoded_name_to_normal(const unsigned char *buf, char *output, int outputsize) {
-  int len;
-  char *p;
-
-  p = output;
-
-  /* Special case: keep the trailing dot only for the name ".". */
-  if (buf[0] == 0) {
-    if (p + 2 > output + outputsize)
-      return -1;
-    *p++ = '.';
-    *p++ = '\0';
-    return 0;
-  }
-
-  while ((len = buf[0]) != 0) {
-    /* Add a dot before every component but the first. */
-    if (p > output) {
-      if (p + 1 > output + outputsize)
-        return -1;
-      *p++ = '.';
-    }
-
-    if (p + len > output + outputsize)
-      return -1;
-    memcpy(p, buf + 1, len);
-    p += len;
-    buf += 1 + len;
-  }
-
-  if (p + 1 > output + outputsize)
-    return -1;
-  *p++ = '\0';
-
-  return 0;
-}
-
-
-// Takes a pointer to the start of a DNS name inside a packet. It makes
-// sure that there is enough space in the name, deals with compression, etc.
-static int advance_past_dns_name(u8 *buf, int buflen, int curbuf,
-                          int *nameloc) {
-  int compression=0;
-
-  if (curbuf <= 0 || curbuf >= buflen) return -1;
-
-  if ((buf[curbuf] & 0xc0)) {
-    // Need 2 bytes for compression info
-    if (curbuf + 1 >= buflen) return -1;
-
-    // Compression is OK
-    compression = curbuf+2;
-    curbuf = (buf[curbuf+1] + (buf[curbuf] << 8)) & 0x3FFF;
-    if (curbuf < 0 || curbuf >= buflen) return -1;
-  }
-
-  if (nameloc != NULL) *nameloc = curbuf;
-
-  while(buf[curbuf]) {
-    if (curbuf + buf[curbuf] >= buflen || buf[curbuf] <= 0) return -1;
-    curbuf += buf[curbuf] + 1;
-  }
-
-  if (compression) return compression;
-  else return curbuf+1;
-}
-
 // Nsock read handler. One nsock read for each DNS server exists at each
 // time. This function uses various helper functions as defined above.
-static void read_evt_handler(nsock_pool nsp, nsock_event evt, void *nothing) {
+static void read_evt_handler(nsock_pool nsp, nsock_event evt, void *) {
   u8 *buf;
-  int buflen, curbuf=0;
-  int i, nameloc, rdlen, atype, aclass;
-  int errcode=0;
-  int queries, answers;
-  u16 packet_id;
+  int buflen;
 
   if (total_reqs >= 1)
     nsock_read(nsp, nse_iod(evt), read_evt_handler, -1, NULL);
@@ -714,120 +702,102 @@ static void read_evt_handler(nsock_pool nsp, nsock_event evt, void *nothing) {
 
   buf = (unsigned char *) nse_readbuf(evt, &buflen);
 
-  // Size of header is 12, and we must have additional data as well
-  if (buflen <= 12) return;
-
-  packet_id = buf[1] + (buf[0] << 8);
-
-  // Check that this is a response, standard query, and that no truncation was performed
-  // 0xFA == 11111010 (we're not concerned with AA or RD bits)
-  if ((buf[2] & 0xFA) != 0x80) return;
-
-  // Check that the zero field is all zeros and there is no error condition.
-  // We don't care if recursion is available or not since we might be querying
-  // an authoritative DNS server.
-  if (buf[3] != 0x80 && buf[3] != 0) {
-    if ((buf[3] & 0xF) == 2) errcode = 2;
-    else if ((buf[3] & 0xF) == 3) errcode = 3;
-    else return;
-  }
-
-  queries = buf[5] + (buf[4] << 8);
-  answers = buf[7] + (buf[6] << 8);
+  DNS::Packet p;
+  size_t readed_bytes = p.parseFromBuffer(buf, buflen);
+  if(readed_bytes < DNS::DATA) return;
 
-  // With a normal resolution, we should have 1+ queries and 1+ answers.
-  // If the domain doesn't resolve (NXDOMAIN or SERVFAIL) we should have
-  // 1+ queries and 0 answers:
-  if (errcode) {
-    int found;
-
-    // NXDomain means we're finished (doesn't exist for sure)
-    // but SERVFAIL might just mean a server timeout
-    found = process_result(0, NULL, errcode == 3 ? ACTION_FINISHED : ACTION_TIMEOUT, packet_id);
+  // We should have 1+ queries:
+  u16 &f = p.flags;
+  if(p.queries.empty() || !DNS_HAS_FLAG(f, DNS::RESPONSE) ||
+     !DNS_HAS_FLAG(f, DNS::OP_STANDARD_QUERY) ||
+     (f & DNS::ZERO) || DNS_HAS_ERR(f, DNS::ERR_FORMAT) ||
+     DNS_HAS_ERR(f, DNS::ERR_NOT_IMPLEMENTED) || DNS_HAS_ERR(f, DNS::ERR_REFUSED))
+    return;
 
-    if (errcode == 2 && found) {
-      if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: SERVFAIL <id = %d>\n", packet_id);
-      stat_sf++;
-    } else if (errcode == 3 && found) {
-      if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: NXDOMAIN <id = %d>\n", packet_id);
+  if (DNS_HAS_ERR(f, DNS::ERR_NAME))
+  {
+    sockaddr_storage discard;
+    if(process_result(discard, "", ACTION_FINISHED, p.id))
+    {
+      if (o.debugging >= TRACE_DEBUG_LEVEL)
+        log_write(LOG_STDOUT, "mass_rdns: NXDOMAIN <id = %d>\n", p.id);
       output_summary();
       stat_nx++;
-  }
+    }
 
     return;
   }
 
-  if (queries <= 0 || answers <= 0) return;
-
-  curbuf = 12;
-
-  // Need to safely skip past QUERY section
-
-  for (i=0; i<queries; i++) {
-    curbuf = advance_past_dns_name(buf, buflen, curbuf, &nameloc);
-    if (curbuf == -1) return;
-
-    // Make sure we have the QTYPE and QCLASS fields
-    if (curbuf + 4 >= buflen) return;
-    curbuf += 4;
-      }
-
-  // We're now at the ANSWER section
-
-  for (i=0; i<answers; i++) {
-    curbuf = advance_past_dns_name(buf, buflen, curbuf, &nameloc);
-    if (curbuf == -1) return;
-
-    // Make sure we have the TYPE (2), CLASS (2), TTL (4), and
-    // RDLENGTH (2) fields
-    if (curbuf + 10 >= buflen) return;
-
-    atype = buf[curbuf+1] + (buf[curbuf+0] << 8);
-    aclass = buf[curbuf+3] + (buf[curbuf+2] << 8);
-    rdlen = buf[curbuf+9] + (buf[curbuf+8] << 8);
-    curbuf += 10;
-
-    if (atype == 12 && aclass == 1) {
-      // TYPE 12 is PTR
-      struct in_addr ia;
-      char outbuf[512];
-
-      ia.s_addr = parse_inaddr_arpa(buf+nameloc, buflen-nameloc);
-      if (ia.s_addr == 0) return;
-
-      curbuf = advance_past_dns_name(buf, buflen, curbuf, &nameloc);
-      if (curbuf == -1 || curbuf > buflen) return;
+  if (DNS_HAS_ERR(f, DNS::ERR_SERVFAIL))
+  {
+    sockaddr_storage discard;
+    if (process_result(discard, "", ACTION_TIMEOUT, p.id))
+    {
+      if (o.debugging >= TRACE_DEBUG_LEVEL)
+        log_write(LOG_STDOUT, "mass_rdns: SERVFAIL <id = %d>\n", p.id);
+      stat_sf++;
+    }
 
-      if (encoded_name_to_normal(buf+nameloc, outbuf, sizeof(outbuf)) == -1) return;
+    return;
+  }
 
-      if (process_result(ia.s_addr, outbuf, ACTION_FINISHED, packet_id)) {
-        if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: OK MATCHED <%s> to <%s>\n", inet_ntoa(ia), outbuf);
-        output_summary();
-        stat_ok++;
+  // If there is no errors and no answhere stop processing the event
+  if(p.answers.empty()) return;
+
+  for(std::list<DNS::Answer>::const_iterator it = p.answers.begin();
+      it != p.answers.end(); ++it )
+  {
+    const DNS::Answer &a = *it;
+    if(a.record_class == DNS::IN)
+    {
+      switch(a.record_type)
+      {
+        case DNS::PTR:
+        {
+          DNS::PTR_Record * ptr = static_cast<DNS::PTR_Record *>(a.record);
+
+          sockaddr_storage ip;
+          if(DNS::Factory::ptrToIp(a.name, ip))
+            if (process_result(ip, ptr->value, ACTION_FINISHED, p.id))
+            {
+              if (o.debugging >= TRACE_DEBUG_LEVEL)
+              {
+                char ipstr[INET6_ADDRSTRLEN];
+                sockaddr_storage_iptop(&ip, ipstr);
+                log_write(LOG_STDOUT, "mass_rdns: OK MATCHED <%s> to <%s>\n",
+                          ipstr,
+                          ptr->value.c_str());
+              }
+              output_summary();
+              stat_ok++;
+            }
+          break;
+        }
+        case DNS::CNAME:
+        {
+          sockaddr_storage ip;
+          if(DNS::Factory::ptrToIp(a.name, ip))
+          {
+            if (o.debugging >= TRACE_DEBUG_LEVEL)
+            {
+              char ipstr[INET6_ADDRSTRLEN];
+              sockaddr_storage_iptop(&ip, ipstr);
+              log_write(LOG_STDOUT, "mass_rdns: CNAME found for <%s>\n", ipstr);
+            }
+            process_result(ip, "", ACTION_CNAME_LIST, p.id);
+          }
+          break;
+        }
+        default:
+          break;
       }
-    } else if (atype == 5 && aclass == 1) {
-      // TYPE 5 is CNAME
-      struct in_addr ia;
-
-      ia.s_addr = parse_inaddr_arpa(buf+nameloc, buflen-nameloc);
-      if (ia.s_addr == 0) return;
-
-      if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: CNAME found for <%s>\n", inet_ntoa(ia));
-      process_result(ia.s_addr, NULL, ACTION_CNAME_LIST, packet_id);
-    } else {
-      if (rdlen < 0 || rdlen + curbuf >= buflen) return;
-      curbuf += rdlen;
     }
-
-    if (curbuf >= buflen) return;
   }
-
 }
 
 
 // nsock connect handler - Empty because it doesn't really need to do anything...
-static void connect_evt_handler(nsock_pool nsp, nsock_event evt, void *servers) {
-}
+static void connect_evt_handler(nsock_pool, nsock_event, void *) {}
 
 
 // Adds DNS servers to the dns_server list. They can be separated by
@@ -976,7 +946,7 @@ void win32_read_registry() {
   }
 
 }
-#endif
+#endif // WIN32
 
 
 
@@ -986,7 +956,7 @@ static void parse_resolvdotconf() {
   FILE *fp;
   char buf[2048], *tp;
   char fmt[32];
-  char ipaddr[INET6_ADDRSTRLEN];
+  char ipaddr[INET6_ADDRSTRLEN+1];
 
   fp = fopen("/etc/resolv.conf", "r");
   if (fp == NULL) {
@@ -994,7 +964,7 @@ static void parse_resolvdotconf() {
     return;
   }
 
-  Snprintf(fmt, sizeof(fmt), "nameserver %%%us", INET6_ADDRSTRLEN-1);
+  Snprintf(fmt, sizeof(fmt), "nameserver %%%us", INET6_ADDRSTRLEN);
 
   while (fgets(buf, sizeof(buf), fp)) {
     tp = buf;
@@ -1016,8 +986,8 @@ static void parse_resolvdotconf() {
 
 static void parse_etchosts(const char *fname) {
   FILE *fp;
-  char buf[2048], hname[256], ipaddrstr[16], *tp;
-  struct in_addr ia;
+  char buf[2048], hname[256], ipaddrstr[INET6_ADDRSTRLEN+1], *tp;
+  sockaddr_storage ia;
 
   fp = fopen(fname, "r");
   if (fp == NULL) return; // silently is OK
@@ -1033,74 +1003,29 @@ static void parse_etchosts(const char *fname) {
     // Skip any leading whitespace
     while (*tp == ' ' || *tp == '\t') tp++;
 
-    if (sscanf(tp, "%15s %255s", ipaddrstr, hname) == 2) {
-      if (inet_pton(AF_INET, ipaddrstr, &ia))
-        addto_etchosts(ia.s_addr, hname);
-    }
-  }
-
-  fclose(fp);
-}
-
-/* Executed when the DNS cache is full, ages entries
- * and removes any with a cache hit of 0 (the least used) */
-bool remove_and_age(host_elem &host) {
-  if(host.cache_hits) {
-     host.cache_hits /=2;
-     return false;
-  } else
-     return true;
-}
-
-/* Add to the dns cache. If there are too many entries
- * we age and remove the least frequently used ones to
- * make more space. */
-static void addto_etchosts(u32 ip, const char *hname) {
-  static u16 total_size = 0;
-  std::list<host_elem>::iterator it;
-  host_elem he;
-  int i;
-
-  if(lookup_etchosts(ip) != NULL)
-    return;
-
-  while(total_size >= HASH_TABLE_SIZE) {
-    for(i = 0; i < HASH_TABLE_SIZE; i++) {
-      while((it = find_if(etchosts[i].begin(), etchosts[i].end(), remove_and_age)) != etchosts[i].end()) {
-        etchosts[i].erase(it);
-        /* We don't want total_size to become out of sync with the actual number
-           of entries. */
-        assert(total_size > 0);
-        total_size--;
+    std::stringstream pattern;
+    pattern << "%" << INET6_ADDRSTRLEN << "s %255s";
+    if (sscanf(tp, pattern.str().c_str(), ipaddrstr, hname) == 2)
+      if (sockaddr_storage_inet_pton(ipaddrstr, &ia))
+      {
+        const std::string hname_ = hname;
+        host_cache.add(ia, hname_);
       }
-    }
   }
-  he.name = hname;
-  he.addr = ip;
-  he.cache_hits = 0;
-  etchosts[IP_HASH(ip)].push_back(he);
-  total_size++;
-}
 
-/* Search for a hostname in the cache and increment
- * its cache hit counter if found */
-static const char *lookup_etchosts(u32 ip) {
-  std::list<host_elem>::iterator hostI;
-  int localIP_Hash = IP_HASH(ip);
-  for(hostI = etchosts[localIP_Hash].begin(); hostI != etchosts[localIP_Hash].end(); hostI++) {
-    if (hostI->addr == ip) {
-      if(hostI->cache_hits < UCHAR_MAX)
-        hostI->cache_hits++;
-      return hostI->name.c_str();
-    }
-  }
-  return NULL;
+  fclose(fp);
 }
 
 /* External interface to dns cache */
-const char *lookup_cached_host(u32 ip) {
-  const char *tmp = lookup_etchosts(ip);
-  return tmp;
+int lookup_cached_host(const struct sockaddr_storage * ip, char *name, size_t maxlen = HOST_NAME_MAX)
+{
+  std::string str_name;
+  if (host_cache.lookup(*ip, str_name))
+  {
+    strncpy(name, str_name.c_str(), maxlen);
+    return 1;
+  }
+  return 0;
 }
 
 static void etchosts_init(void) {
@@ -1129,7 +1054,7 @@ static void etchosts_init(void) {
 
 #else
   parse_etchosts("/etc/hosts");
-#endif
+#endif // WIN32
 }
 
 /* Initialize the global servs list of DNS servers. If the --dns-servers option
@@ -1175,24 +1100,28 @@ static void nmap_mass_rdns_core(Target **targets, int num_targets) {
   // If necessary, set up the dns server list
   init_servs();
 
-  if (servs.size() == 0 && firstrun) error("mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers");
+  if (servs.size() == 0 && firstrun) error("mass_dns: warning: Unable to "
+                                           "determine any DNS servers. Reverse"
+                                           " DNS is disabled. Try using "
+                                           "--system-dns or specify valid "
+                                           "servers with --dns-servers");
 
 
-  // If necessary, set up the /etc/hosts hashtable
+  // If necessary, read /etc/hosts and put entries into the hashtable
   etchosts_init();
 
 
   total_reqs = 0;
-  id_counter = get_random_u16();
 
   // Set up the request structure
-  for(hostI = targets; hostI < targets+num_targets; hostI++) {
+  for(hostI = targets; hostI < targets+num_targets; hostI++)
+  {
     if (!((*hostI)->flags & HOST_UP) && !o.resolve_all) continue;
 
-    // See if it's in /etc/hosts or cached
-    assert((*hostI)->af() == AF_INET);
-    tpname = lookup_etchosts((u32) (*hostI)->v4hostip()->s_addr);
-    if (tpname) {
+    // See if it's cached
+    std::string res;
+    if (host_cache.lookup(*(*hostI)->TargetSockAddr(), res)) {
+      tpname = res.c_str();
       (*hostI)->setHostName(tpname);
       continue;
     }
@@ -1201,7 +1130,7 @@ static void nmap_mass_rdns_core(Target **targets, int num_targets) {
     tpreq->targ = *hostI;
     tpreq->tries = 0;
     tpreq->servers_tried = 0;
-    tpreq->id = id_counter++;
+    tpreq->id = DNS::Factory::progressiveId;
 
     new_reqs.push_back(tpreq);
 
@@ -1342,8 +1271,7 @@ void nmap_mass_rdns(Target **targets, int num_targets) {
 
   stat_actual = stat_ok = stat_nx = stat_sf = stat_trans = stat_dropped = stat_cname = 0;
 
-  // mass_dns only supports IPv4.
-  if (o.mass_dns && o.af() == AF_INET)
+  if (o.mass_dns)
     nmap_mass_rdns_core(targets, num_targets);
   else
     nmap_system_rdns_core(targets, num_targets);
@@ -1352,7 +1280,7 @@ void nmap_mass_rdns(Target **targets, int num_targets) {
 
   if (stat_actual > 0) {
     if (o.debugging || o.verbose >= 3) {
-      if (o.mass_dns && o.af() == AF_INET) {
+      if (o.mass_dns) {
         // #:  Number of DNS servers used
         // OK: Number of fully reverse resolved queries
         // NX: Number of confirmations of 'No such reverse domain eXists'
@@ -1389,3 +1317,338 @@ std::list<std::string> get_dns_servers() {
   }
   return serverList;
 }
+
+bool DNS::Factory::ipToPtr(const sockaddr_storage &ip, std::string &ptr)
+{
+  switch (ip.ss_family) {
+    case AF_INET:
+    {
+      ptr.clear();
+      char ipv4_c[INET_ADDRSTRLEN];
+      if(!sockaddr_storage_iptop(&ip, ipv4_c)) return false;
+
+      std::string ipv4 = ipv4_c;
+      std::string octet;
+      for (std::string::const_reverse_iterator c=ipv4.rbegin(); c != ipv4.rend(); ++c)
+        if((*c)=='.')
+        {
+          ptr += octet + ".";
+          octet.clear();
+        }
+        else
+          octet = (*c) + octet;
+
+      ptr += octet + IPV4_PTR_DOMAIN;
+
+      break;
+    }
+    case AF_INET6:
+    {
+      ptr.clear();
+      const struct sockaddr_in6 &s6 = (const struct sockaddr_in6 &) ip;
+      const u8 * ipv6 = s6.sin6_addr.__in6_u.__u6_addr8;
+      for (short i=15; i>=0; --i)
+      {
+        char tmp[3];
+        sprintf(tmp, "%02x", ipv6[i]);
+        ptr += '.';
+        ptr += tmp[1];
+        ptr += '.';
+        ptr += tmp[0];
+      }
+      ptr.erase(ptr.begin());
+      ptr += IPV6_PTR_DOMAIN;
+      break;
+    }
+    default:
+      return false;
+  }
+  return true;
+}
+bool DNS::Factory::ptrToIp(const std::string &ptr, sockaddr_storage &ip)
+{
+  std::string ip_str;
+
+  size_t pos = ptr.rfind(IPV6_PTR_DOMAIN);
+  if(pos != std::string::npos)
+  {
+    u8 counter = 0;
+    for (std::string::const_reverse_iterator it = ptr.rend()-pos; it != ptr.rend(); ++it)
+    {
+      const char &c = *it;
+      if(c != '.')
+      {
+        ip_str += c;
+        if(++counter==4) counter=0, ip_str+=':';
+      }
+    }
+
+    std::string::iterator it = ip_str.end()-1;
+    if( *it == ':') ip_str.erase(it);
+  }
+
+  std::string mptr = '.' + ptr;
+  pos = mptr.rfind(IPV4_PTR_DOMAIN);
+  if(pos != std::string::npos)
+  {
+
+    std::string octet;
+    for (std::string::const_reverse_iterator it = mptr.rend()-pos; it != mptr.rend(); ++it)
+    {
+      const char &c = *it;
+      if(c == '.')
+      {
+        std::reverse(octet.begin(), octet.end());
+        ip_str += octet + '.';
+        octet.clear();
+      }
+      else octet += c;
+    }
+
+    std::string::iterator it = ip_str.end()-1;
+    if( *it == '.') ip_str.erase(it);
+  }
+
+  if(ip_str.empty())
+    return false;
+
+  sockaddr_storage_inet_pton(ip_str.c_str(), &ip);
+  return true;
+}
+size_t DNS::Factory::buildSimpleRequest(const std::string &name, RECORD_TYPE rt, u8 *buf, size_t maxlen)
+{
+  size_t ret=0 , tmp=0;
+  DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(progressiveId++, buf, ID, maxlen)); // Postincrement inmportant here
+  DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(OP_STANDARD_QUERY | RECURSION_DESIRED, buf, FLAGS_OFFSET, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(1, buf, QDCOUNT, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(0, buf, ANCOUNT, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(0, buf, NSCOUNT, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(0, buf, ARCOUNT, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, putDomainName(name, buf, DATA, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(rt, buf, ret, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, putUnsignedShort(IN, buf, ret, maxlen));
+
+  return ret;
+}
+size_t DNS::Factory::buildReverseRequest(const sockaddr_storage &ip, u8 *buf, size_t maxlen)
+{
+  std::string name;
+  if(ipToPtr(ip,name))
+    return buildSimpleRequest(name, PTR, buf, maxlen);
+  return 0;
+}
+size_t DNS::Factory::putUnsignedShort(u16 num, u8 *buf, size_t offset, size_t maxlen)
+{
+  size_t max_access = offset+1;
+  if(buf && (maxlen > max_access))
+  {
+    buf[offset] = (num >> 8) & 0xFF;
+    buf[max_access] = num & 0xFF;
+    return 2;
+  }
+
+  return 0;
+}
+size_t DNS::Factory::putDomainName(const std::string &name, u8 *buf, size_t offset, size_t maxlen)
+{
+  size_t ret=0;
+  if( !( buf && (maxlen > (offset + name.length() + 1))) ) return ret;
+
+  std::string namew = name + ".";
+  std::string accumulator;
+  for (std::string::const_iterator c=namew.begin(); c != namew.end(); ++c)
+  {
+    if((*c)=='.')
+    {
+      u8 lenght = accumulator.length();
+      *(buf+offset+ret) = lenght;
+      ret += 1;
+
+      memcpy(buf+offset+ret, accumulator.c_str(), lenght);
+      ret += lenght;
+      accumulator.clear();
+    }
+    else
+      accumulator += (*c);
+  }
+
+  *(buf+offset+ret) = 0;
+  ret += 1;
+
+  return ret;
+}
+size_t DNS::Factory::parseUnsignedShort(u16 &num, const u8 *buf, size_t offset, size_t maxlen)
+{
+  size_t max_access = offset+1;
+  if(buf && (maxlen > max_access))
+  {
+    const u8 * n = buf + offset;
+    num = n[1] + (n[0]<<8);
+    return 2;
+  }
+
+  return 0;
+}
+size_t DNS::Factory::parseUnsignedInt(u32 &num, const u8 *buf, size_t offset, size_t maxlen)
+{
+  size_t max_access = offset+3;
+  if(buf && (maxlen > max_access))
+  {
+    const u8 * n = buf + offset;
+    num = n[3] + (n[2]<<8) + (n[1]<<16) + (n[0]<<24);
+    return 4;
+  }
+
+  return 0;
+}
+size_t DNS::Factory::parseDomainName(std::string &name, const u8 *buf, size_t offset, size_t maxlen)
+{
+  size_t tmp, ret = 0;
+
+  name.clear();
+  while(u8 label_length = buf[offset+ret++]) // Postincrement important here
+  {
+    if((label_length & COMPRESSED_NAME) == COMPRESSED_NAME)
+    {
+      --ret; // The byte it's part of the pointer, wasn't really consumed yet
+      u16 real_offset;
+      DNS_CHECK_ACCUMLATE(ret, tmp, parseUnsignedShort(real_offset, buf, offset+ret, maxlen));
+      real_offset -= COMPRESSED_NAME<<8;
+      if( real_offset < offset)
+      {
+        std::string val;
+        DNS_CHECK_ACCUMLATE(tmp, tmp, parseDomainName(val, buf, real_offset, maxlen));
+        name+=val;
+        return ret;
+      }
+      else return 0;
+    }
+
+    for(u8 i=0; i<label_length; ++i)
+    {
+      size_t index = offset+ret++;  // Postincrement important here
+      DNS_CHECK_UPPER_BOUND(index, maxlen);
+      name += buf[index];
+    }
+    name += '.';
+  }
+
+  std::string::iterator it = name.end()-1;
+  if( *it == '.') name.erase(it);
+
+  return ret;
+}
+
+size_t DNS::A_Record::parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen)
+{
+  size_t tmp, ret = 0;
+  u32 num;
+  DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedInt(num, buf, offset, maxlen));
+
+  memset(&value, 0, sizeof(value));
+  struct sockaddr_in * ip4addr = (sockaddr_in *) &value;
+  ip4addr->sin_family = AF_INET;
+  ip4addr->sin_addr.s_addr = htonl(num);
+
+  return ret;
+}
+
+size_t DNS::Query::parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen)
+{
+  size_t ret=0;
+
+  if (buf && ((maxlen - offset) > 5))
+  {
+    size_t tmp=0;
+    DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseDomainName(name, buf, offset+ret, maxlen));
+    DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(record_type, buf, offset+ret, maxlen));
+    DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(record_class, buf, offset+ret, maxlen));
+  }
+
+  return ret;
+}
+
+size_t DNS::Answer::parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen)
+{
+  size_t ret=0;
+
+  if (buf && ((maxlen - offset) > 7))
+  {
+    size_t tmp;
+    DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseDomainName(name, buf, offset+ret, maxlen));
+    DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(record_type, buf, offset+ret, maxlen));
+    DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(record_class, buf, offset+ret, maxlen));
+    DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedInt(ttl, buf, offset+ret, maxlen));
+    DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(length, buf, offset+ret, maxlen));
+
+    DNS_CHECK_UPPER_BOUND(offset+ret+length, maxlen);
+
+    switch(record_type)
+    {
+    case A:
+    {
+      record = new A_Record();
+      break;
+    }
+    case CNAME:
+    {
+      record = new CNAME_Record();
+      break;
+    }
+    case PTR:
+    {
+      record = new PTR_Record();
+      break;
+    }
+    default:
+      return 0;
+    }
+
+    DNS_CHECK_ACCUMLATE(ret, tmp, record->parseFromBuffer(buf, offset+ret, maxlen));
+  }
+
+  return ret;
+}
+DNS::Answer& DNS::Answer::operator=(const Answer &r)
+{
+  name = r.name;
+  record_type = r.record_type;
+  record_class = r.record_class;
+  ttl = r.ttl;
+  length = r.length;
+  record = r.record->clone();
+  return *this;
+}
+
+size_t DNS::Packet::parseFromBuffer(const u8 *buf, size_t maxlen)
+{
+  if( !buf || maxlen < DATA) return 0;
+
+  size_t tmp, ret = 0;
+  DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(id, buf, ID, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(flags, buf, FLAGS_OFFSET, maxlen));
+
+  u16 queries_counter, answers_counter, authorities_counter, additionals_counter;
+  DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(queries_counter, buf, QDCOUNT, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(answers_counter, buf, ANCOUNT, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(authorities_counter, buf, NSCOUNT, maxlen));
+  DNS_CHECK_ACCUMLATE(ret, tmp, Factory::parseUnsignedShort(additionals_counter, buf, ARCOUNT, maxlen));
+
+  queries.clear();
+  for(u16 i=0; i<queries_counter; ++i)
+  {
+    Query q;
+    DNS_CHECK_ACCUMLATE(ret, tmp, q.parseFromBuffer(buf, ret, maxlen));
+    queries.push_back(q);
+  }
+
+  answers.clear();
+  for(u16 i=0; i<answers_counter; ++i)
+  {
+    Answer a;
+    DNS_CHECK_ACCUMLATE(ret, tmp, a.parseFromBuffer(buf, ret, maxlen));
+    answers.push_back(a);
+  };
+
+  return ret;
+}
diff --git a/nmap_dns.h b/nmap_dns.h
index 08c32ec..8912a89 100644
--- a/nmap_dns.h
+++ b/nmap_dns.h
@@ -124,13 +124,188 @@
 
 class Target;
 
-#include "nbase.h"
+#include "nbase/nbase.h"
 
 #include <string>
 #include <list>
 
+#include <algorithm>
+#include <sstream>
+
+
+namespace DNS
+{
+
+#define DNS_CHECK_ACCUMLATE(accumulator, tmp, exp) \
+  do { tmp = exp; if(tmp < 1) return 0 ; accumulator += tmp;} while(0)
+
+#define DNS_CHECK_UPPER_BOUND(accumulator, max)\
+  do { if(accumulator > max) return 0; } while(0)
+
+#define DNS_HAS_FLAG(v,flag) ((v&flag)==flag)
+
+#define DNS_HAS_ERR(v, err) ((v&DNS::ERR_ALL)==err)
+
+typedef enum
+{
+  ID = 0,
+  FLAGS_OFFSET = 2,
+  QDCOUNT = 4,
+  ANCOUNT = 6,
+  NSCOUNT = 8,
+  ARCOUNT = 10,
+  DATA = 12
+} HEADER_OFFSET;
+
+typedef enum {
+  ERR_ALL = 0x0007,
+  CHECKING_DISABLED = 0x0010,
+  AUTHENTICATED_DATA = 0x0020,
+  ZERO = 0x0070,
+  RECURSION_AVAILABLE = 0x0080,
+  RECURSION_DESIRED = 0x0100,
+  TRUNCATED = 0x0200,
+  AUTHORITATIVE_ANSWER = 0x0400,
+  OP_STANDARD_QUERY = 0x0000,
+  OP_INVERSE_QUERY = 0x0800, // Obsoleted in RFC 3425
+  OP_SERVER_STATUS = 0x1000,
+  RESPONSE = 0x8000
+} FLAGS;
+
+typedef enum {
+  ERR_NO = 0x0000,
+  ERR_FORMAT = 0x0001,
+  ERR_SERVFAIL = 0x0002,
+  ERR_NAME = 0x0003,
+  ERR_NOT_IMPLEMENTED = 0x0004,
+  ERR_REFUSED = 0x0005,
+} ERRORS;
+
+typedef enum {
+  A = 1,
+  CNAME = 5,
+  PTR = 12,
+  AAAA = 28,
+} RECORD_TYPE;
+
+typedef enum {
+  IN = 1
+} RECORD_CLASS;
+
+const u8 COMPRESSED_NAME = 0xc0;
+
+const std::string IPV4_PTR_DOMAIN = ".in-addr.arpa";
+const std::string IPV6_PTR_DOMAIN = ".ip6.arpa";
+
+class Factory
+{
+public:
+  static u16 progressiveId;
+  static bool ipToPtr(const sockaddr_storage &ip, std::string &ptr);
+  static bool ptrToIp(const std::string &ptr, sockaddr_storage &ip);
+  static size_t buildSimpleRequest(const std::string &name, RECORD_TYPE rt, u8 *buf, size_t maxlen);
+  static size_t buildReverseRequest(const sockaddr_storage &ip, u8 *buf, size_t maxlen);
+  static size_t putUnsignedShort(u16 num, u8 *buf, size_t offset, size_t maxlen);
+  static size_t putDomainName(const std::string &name, u8 *buf, size_t offset, size_t maxlen);
+  static size_t parseUnsignedShort(u16 &num, const u8 *buf, size_t offset, size_t maxlen);
+  static size_t parseUnsignedInt(u32 &num, const u8 *buf, size_t offset, size_t maxlen);
+  static size_t parseDomainName(std::string &name, const u8 *buf, size_t offset, size_t maxlen);
+};
+
+class Record
+{
+public:
+  virtual Record * clone() = 0;
+  virtual ~Record() {}
+  virtual size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen) = 0;
+};
+
+class A_Record : public Record
+{
+public:
+  sockaddr_storage value;
+  Record * clone() { return new A_Record(*this); }
+  ~A_Record() {}
+  size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen);
+};
+
+class PTR_Record : public Record
+{
+public:
+  std::string value;
+  Record * clone() { return new PTR_Record(*this); }
+  ~PTR_Record() {}
+  size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen)
+  {
+    return Factory::parseDomainName(value, buf, offset, maxlen);
+  }
+};
+
+class CNAME_Record : public Record
+{
+public:
+  std::string value;
+  Record * clone() { return new CNAME_Record(*this); }
+  ~CNAME_Record() {}
+  size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen)
+  {
+    return Factory::parseDomainName(value, buf, offset, maxlen);
+  }
+};
+
+class Query
+{
+public:
+  std::string name;
+  u16 record_type;
+  u16 record_class;
+
+  size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen);
+};
+
+class Answer
+{
+public:
+  Answer() : record(NULL) {}
+  Answer(const Answer &c) : name(c.name), record_type(c.record_type),
+    record_class(c.record_class), ttl(c.ttl), length(c.length),
+    record(c.record->clone()) {}
+  ~Answer() { delete record; }
+
+  std::string name;
+  u16 record_type;
+  u16 record_class;
+  u32 ttl;
+  u16 length;
+  Record * record;
+
+  // Populate the object reading from buffer and returns "consumed" bytes
+  size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen);
+  Answer& operator=(const Answer &r);
+};
+
+class Packet
+{
+public:
+  Packet() : id(0), flags(0) {}
+  ~Packet() {}
+
+  void addFlags(FLAGS fl){ flags |= fl; }
+  void removeFlags(FLAGS fl){ flags &= ~fl; }
+  void resetFlags() { flags = 0; }
+  size_t writeToBuffer(u8 *buf, size_t maxlen);
+  size_t parseFromBuffer(const u8 *buf, size_t maxlen);
+
+  u16 id;
+  u16 flags;
+  std::list<Query> queries;
+  std::list<Answer> answers;
+};
+
+}
+
 void nmap_mass_rdns(Target ** targets, int num_targets);
-const char *lookup_cached_host(u32 ip);
+int lookup_cached_host(const struct sockaddr_storage * ip, char * name, size_t maxlen);
 
 std::list<std::string> get_dns_servers();
 
diff --git a/tests/nmap_dns_test.cc b/tests/nmap_dns_test.cc
new file mode 100644
index 0000000..91aa2b4
--- /dev/null
+++ b/tests/nmap_dns_test.cc
@@ -0,0 +1,263 @@
+
+/***************************************************************************
+ * dns_request_generation.cc -- Tests DNS request generation               *
+ *                                                                         *
+ ***********************IMPORTANT NMAP LICENSE TERMS************************
+ *                                                                         *
+ * The Nmap Security Scanner is (C) 1996-2015 Insecure.Com LLC. Nmap is    *
+ * also a registered trademark of Insecure.Com LLC.  This program is free  *
+ * software; you may redistribute and/or modify it under the terms of the  *
+ * GNU General Public License as published by the Free Software            *
+ * Foundation; Version 2 ("GPL"), BUT ONLY WITH ALL OF THE CLARIFICATIONS  *
+ * AND EXCEPTIONS DESCRIBED HEREIN.  This guarantees your right to use,    *
+ * modify, and redistribute this software under certain conditions.  If    *
+ * you wish to embed Nmap technology into proprietary software, we sell    *
+ * alternative licenses (contact sales () nmap com).  Dozens of software      *
+ * vendors already license Nmap technology such as host discovery, port    *
+ * scanning, OS detection, version detection, and the Nmap Scripting       *
+ * Engine.                                                                 *
+ *                                                                         *
+ * Note that the GPL places important restrictions on "derivative works",  *
+ * yet it does not provide a detailed definition of that term.  To avoid   *
+ * misunderstandings, we interpret that term as broadly as copyright law   *
+ * allows.  For example, we consider an application to constitute a        *
+ * derivative work for the purpose of this license if it does any of the   *
+ * following with any software or content covered by this license          *
+ * ("Covered Software"):                                                   *
+ *                                                                         *
+ * o Integrates source code from Covered Software.                         *
+ *                                                                         *
+ * o Reads or includes copyrighted data files, such as Nmap's nmap-os-db   *
+ * or nmap-service-probes.                                                 *
+ *                                                                         *
+ * o Is designed specifically to execute Covered Software and parse the    *
+ * results (as opposed to typical shell or execution-menu apps, which will *
+ * execute anything you tell them to).                                     *
+ *                                                                         *
+ * o Includes Covered Software in a proprietary executable installer.  The *
+ * installers produced by InstallShield are an example of this.  Including *
+ * Nmap with other software in compressed or archival form does not        *
+ * trigger this provision, provided appropriate open source decompression  *
+ * or de-archiving software is widely available for no charge.  For the    *
+ * purposes of this license, an installer is considered to include Covered *
+ * Software even if it actually retrieves a copy of Covered Software from  *
+ * another source during runtime (such as by downloading it from the       *
+ * Internet).                                                              *
+ *                                                                         *
+ * o Links (statically or dynamically) to a library which does any of the  *
+ * above.                                                                  *
+ *                                                                         *
+ * o Executes a helper program, module, or script to do any of the above.  *
+ *                                                                         *
+ * This list is not exclusive, but is meant to clarify our interpretation  *
+ * of derived works with some common examples.  Other people may interpret *
+ * the plain GPL differently, so we consider this a special exception to   *
+ * the GPL that we apply to Covered Software.  Works which meet any of     *
+ * these conditions must conform to all of the terms of this license,      *
+ * particularly including the GPL Section 3 requirements of providing      *
+ * source code and allowing free redistribution of the work as a whole.    *
+ *                                                                         *
+ * As another special exception to the GPL terms, Insecure.Com LLC grants  *
+ * permission to link the code of this program with any version of the     *
+ * OpenSSL library which is distributed under a license identical to that  *
+ * listed in the included docs/licenses/OpenSSL.txt file, and distribute   *
+ * linked combinations including the two.                                  *
+ *                                                                         *
+ * Any redistribution of Covered Software, including any derived works,    *
+ * must obey and carry forward all of the terms of this license, including *
+ * obeying all GPL rules and restrictions.  For example, source code of    *
+ * the whole work must be provided and free redistribution must be         *
+ * allowed.  All GPL references to "this License", are to be treated as    *
+ * including the terms and conditions of this license text as well.        *
+ *                                                                         *
+ * Because this license imposes special exceptions to the GPL, Covered     *
+ * Work may not be combined (even as part of a larger work) with plain GPL *
+ * software.  The terms, conditions, and exceptions of this license must   *
+ * be included as well.  This license is incompatible with some other open *
+ * source licenses as well.  In some cases we can relicense portions of    *
+ * Nmap or grant special permissions to use it in other open source        *
+ * software.  Please contact fyodor () nmap org with any such requests.       *
+ * Similarly, we don't incorporate incompatible open source software into  *
+ * Covered Software without special permission from the copyright holders. *
+ *                                                                         *
+ * If you have any questions about the licensing restrictions on using     *
+ * Nmap in other works, are happy to help.  As mentioned above, we also    *
+ * offer alternative license to integrate Nmap into proprietary            *
+ * applications and appliances.  These contracts have been sold to dozens  *
+ * of software vendors, and generally include a perpetual license as well  *
+ * as providing for priority support and updates.  They also fund the      *
+ * continued development of Nmap.  Please email sales () nmap com for further *
+ * information.                                                            *
+ *                                                                         *
+ * If you have received a written license agreement or contract for        *
+ * Covered Software stating terms other than these, you may choose to use  *
+ * and redistribute Covered Software under those terms instead of these.   *
+ *                                                                         *
+ * Source is provided to this software because we believe users have a     *
+ * right to know exactly what a program is going to do before they run it. *
+ * This also allows you to audit the software for security holes.          *
+ *                                                                         *
+ * Source code also allows you to port Nmap to new platforms, fix bugs,    *
+ * and add new features.  You are highly encouraged to send your changes   *
+ * to the dev () nmap org mailing list for possible incorporation into the    *
+ * main distribution.  By sending these changes to Fyodor or one of the    *
+ * Insecure.Org development mailing lists, or checking them into the Nmap  *
+ * source code repository, it is understood (unless you specify otherwise) *
+ * that you are offering the Nmap Project (Insecure.Com LLC) the           *
+ * unlimited, non-exclusive right to reuse, modify, and relicense the      *
+ * code.  Nmap will always be available Open Source, but this is important *
+ * because the inability to relicense code has caused devastating problems *
+ * for other Free Software projects (such as KDE and NASM).  We also       *
+ * occasionally relicense the code to third parties as discussed above.    *
+ * If you wish to specify special license conditions of your               *
+ * contributions, just say so when you send them.                          *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful, but     *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of              *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the Nmap      *
+ * license file for more details (it's in a COPYING file included with     *
+ * Nmap, and also available from https://svn.nmap.org/nmap/COPYING)        *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "../nmap_dns.h"
+
+#include <iostream>
+
+#define TEST_INCR(pred,acc) \
+if ( !(pred) ) \
+{ \
+  std::cout << "Test " << #pred << " failed at " << __FILE__ << ":" << __LINE__ << std::endl; \
+  ++acc; \
+}
+
+int main()
+{
+  std::cout << "Testing nmap_dns" << std::endl;
+
+  int ret = 0;
+  std::string target = "scanme.nmap.org";
+  DNS::RECORD_TYPE rt = DNS::A;
+  const size_t buflen = 1500;
+  u8 buf[buflen];
+  size_t reqlen = DNS::Factory::buildSimpleRequest(target, rt, buf, buflen);
+  
+  DNS::Packet p;
+  size_t plen = p.parseFromBuffer(buf, buflen);
+  TEST_INCR(reqlen == plen, ret);
+
+  DNS::Query * q = &*p.queries.begin();
+  TEST_INCR(q->name == target, ret);
+  TEST_INCR(q->record_class == DNS::IN, ret);
+  TEST_INCR(q->record_type == rt, ret);
+
+
+  // This is a possible answere for an A query for scanme.nmap.org
+  const char ipp[] = "45.33.32.156";
+  const size_t answere_len = 49;
+  const u8 answere_buf[] = { 0x92, 0xdc, // Trsnsaction ID
+                       0x81, 0x80, // Flags
+                       0x00, 0x01, // Questions count
+                       0x00, 0x01, // Answers RRs count
+                       0x00, 0x00, // Authorities RRs count
+                       0x00, 0x00, // Additionals RRs count
+                       0x06, // Label lenght <-- [12]
+                       0x73, 0x63, 0x61, 0x6e, 0x6d, 0x65, // "scanme"
+                       0x04, // Label lenght
+                       0x6e, 0x6d, 0x61, 0x70, // "nmap"
+                       0x03, // Label lenght
+                       0x6f, 0x72, 0x67, // "org"
+                       0x00, // Name terminator
+                       0x00, 0x01, // A
+                       0x00, 0x01, // IN
+                       0xc0, 0x0c, // Compressed name pointer to offset 12
+                       0x00, 0x01, // A
+                       0x00, 0x01, // IN
+                       0x00, 0x00, 0x0e, 0x0f, // TTL 3599
+                       0x00, 0x04, // Record Lenght
+                       0x2d, 0x21, 0x20, 0x9c }; // 45.33.32.156
+
+  plen = p.parseFromBuffer(answere_buf, answere_len);
+  TEST_INCR(answere_len == plen, ret);
+
+  q = &*p.queries.begin();
+  TEST_INCR(q->name == target, ret);
+  TEST_INCR(q->record_class == DNS::IN, ret);
+  TEST_INCR(q->record_type == rt, ret );
+
+  DNS::Answer * a = &*p.answers.begin();
+  TEST_INCR(a->name == target, ret );
+  TEST_INCR(a->record_class == DNS::IN, ret);
+  TEST_INCR(a->record_type == DNS::A, ret);
+  TEST_INCR(a->ttl == 3599, ret)
+
+  DNS::A_Record * ar = static_cast<DNS::A_Record *>(a->record);
+  char ar_ipp[INET6_ADDRSTRLEN];
+  sockaddr_storage_iptop(&ar->value, ar_ipp);
+  TEST_INCR(!strcmp(ipp, ar_ipp), ret);
+
+  const size_t ptr_answere_len = 72;
+  std::string ptr_target;
+  TEST_INCR(DNS::Factory::ipToPtr(ar->value, ptr_target), ret);
+  TEST_INCR(ptr_target == "156.32.33.45.in-addr.arpa", ret);
+  const u8 ptr_answere[] = { 0x08, 0xf2, // ID
+                               0x81, 0x80, // Flags
+                               0x00, 0x01, // Questions count
+                               0x00, 0x01, // Answers RRs count
+                               0x00, 0x00, // Authorities RRs count
+                               0x00, 0x00, // Additionals RRs count
+                               0x03, // Label lenght
+                               0x31, 0x35, 0x36, // "156"
+                               0x02, // Label lenght
+                               0x33, 0x32, // "32"
+                               0x02, // Label lenght
+                               0x33, 0x33, // "33"
+                               0x02, // Label lenght
+                               0x34, 0x35, // "45"
+                               0x07, // Label lenght
+                               0x69, 0x6e, 0x2d, 0x61, 0x64, 0x64, 0x72, // "in-addr"
+                               0x04, // Label lenght
+                               0x61, 0x72, 0x70, 0x61, // "arpa"
+                               0x00, // Name terminator
+                               0x00, 0x0c, // PTR
+                               0x00, 0x01, // IN
+                               0xc0, 0x0c, // Compressed name pointer to offset 12
+                               0x00, 0x0c, // PTR
+                               0x00, 0x01, // IN
+                               0x00, 0x01, 0x51, 0x78, // TTL 86392
+                               0x00, 0x11, // Record Lenght
+                               0x06, // Label lenght
+                               0x73, 0x63, 0x61, 0x6e, 0x6d, 0x65, // "scanme"
+                               0x04, // Label lenght
+                               0x6e, 0x6d, 0x61, 0x70, // "nmap"
+                               0x03, // Label lenght
+                               0x6f, 0x72, 0x67, // "org"
+                               0x00 };  // Name terminator
+
+  plen = p.parseFromBuffer(ptr_answere, ptr_answere_len);
+  TEST_INCR(plen == ptr_answere_len, ret);
+  TEST_INCR(p.id == 0x08f2, ret);
+  TEST_INCR(p.flags == 0x8180, ret);
+  TEST_INCR(p.queries.size() == 1, ret);
+  TEST_INCR(p.answers.size() == 1, ret);
+
+  q = &*p.queries.begin();
+  TEST_INCR(q->name == ptr_target, ret);
+  TEST_INCR(q->record_class == DNS::IN, ret);
+  TEST_INCR(q->record_type == DNS::PTR, ret);
+
+  a = &*p.answers.begin();
+  TEST_INCR(a->name == ptr_target, ret);
+  TEST_INCR(a->record_class == DNS::IN, ret);
+  TEST_INCR(a->record_type == DNS::PTR, ret);
+  TEST_INCR(a->length == 0x11, ret);
+  TEST_INCR(a->ttl == 86392, ret);
+
+  DNS::PTR_Record * r = static_cast<DNS::PTR_Record *>(a->record);
+  TEST_INCR(r->value == target, ret);
+
+  if(ret) std::cout << "Testing nmap_dns finished with errors" << std::endl;
+  else std::cout << "Testing nmap_dns finished without errors" << std::endl;
+
+  return ret; // 0 means ok
+}
