Index: dnswriter.hh
===================================================================
--- dnswriter.hh (revision 962)
+++ dnswriter.hh (revision 996)
@@ -84,5 +84,5 @@
 
   void xfrLabel(const string& label, bool compress=false);
-  void xfrText(const string& text);
+  void xfrText(const string& text, bool multi=false);
   void xfrBlob(const string& blob);
   void xfrHexBlob(const string& blob);
Index: dnsparser.hh
===================================================================
--- dnsparser.hh (revision 972)
+++ dnsparser.hh (revision 996)
@@ -110,7 +110,7 @@
   }
 
-  void xfrText(string &text)
-  {
-    text=getText();
+  void xfrText(string &text, bool multi=false)
+  {
+    text=getText(multi);
   }
 
@@ -126,5 +126,5 @@
 
   string getLabel(unsigned int recurs=0);
-  string getText();
+  string getText(bool multi);
 
   uint16_t d_pos;
Index: zoneparser-tng.cc
===================================================================
--- zoneparser-tng.cc (revision 989)
+++ zoneparser-tng.cc (revision 996)
@@ -281,6 +281,5 @@
     }
     catch(...) {
-      cerr<<"Oops, this doesn't look like a qtype, stopping loop\n";
-      break;
+      throw runtime_error("Parsing zone content line: '"+nextpart+"' doesn't look like a qtype, stopping loop");
     }
   }
Index: dnswriter.cc
===================================================================
--- dnswriter.cc (revision 962)
+++ dnswriter.cc (revision 996)
@@ -2,4 +2,5 @@
 #include "misc.hh"
 #include "dnsparser.hh"
+#include <boost/tokenizer.hpp>
 
 DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const string& qname, uint16_t  qtype, uint16_t qclass, uint8_t opcode)
@@ -116,9 +117,20 @@
 }
 
-void DNSPacketWriter::xfrText(const string& text)
-{
-  d_record.push_back(text.length());
-  const uint8_t* ptr=(uint8_t*)(text.c_str());
-  d_record.insert(d_record.end(), ptr, ptr+text.size());
+void DNSPacketWriter::xfrText(const string& text, bool)
+{
+  escaped_list_separator<char> sep('\\', ' ' , '"');
+  tokenizer<escaped_list_separator<char> > tok(text, sep);
+
+  tokenizer<escaped_list_separator<char> >::iterator beg=tok.begin();
+
+  if(beg==tok.end()) {
+    d_record.push_back(0);
+  }
+  else 
+    for(; beg!=tok.end(); ++beg){
+      d_record.push_back(beg->length());
+      const uint8_t* ptr=(uint8_t*)(beg->c_str());
+      d_record.insert(d_record.end(), ptr, ptr+beg->length());
+    }
 }
 
Index: dnsparser.cc
===================================================================
--- dnsparser.cc (revision 972)
+++ dnsparser.cc (revision 996)
@@ -359,14 +359,42 @@
 }
 
-string PacketReader::getText()
+static string txtEscape(const string &name)
+{
+  string ret;
+
+  for(string::const_iterator i=name.begin();i!=name.end();++i)
+    if(*i=='"' || *i=='\\'){
+      ret += '\\';
+      ret += *i;
+    }
+    else
+      ret += *i;
+  return ret;
+}
+
+// exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
+string PacketReader::getText(bool multi)
 {
   string ret;
   ret.reserve(40);
-
-  unsigned char labellen=d_content.at(d_pos++);
-  ret.append(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1); // the end is one beyond the packet
-  d_pos+=labellen;
-  return ret;
-}
+  while(d_pos < d_startrecordpos + d_recordlen ) {
+    if(!ret.empty()) {
+      ret.append(1,' ');
+    }
+    unsigned char labellen=d_content.at(d_pos++);
+    
+    ret.append(1,'"');
+    string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1);
+    
+    ret.append(txtEscape(val)); // the end is one beyond the packet
+    ret.append(1,'"');
+    d_pos+=labellen;
+    if(!multi)
+      break;
+  }
+
+  return ret;
+}
+
 
 void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs) 
Index: rcpgenerator.hh
===================================================================
--- rcpgenerator.hh (revision 802)
+++ rcpgenerator.hh (revision 996)
@@ -51,5 +51,5 @@
 
   void xfrLabel(string& val, bool compress=false);
-  void xfrText(string& val);
+  void xfrText(string& val, bool multi=false);
   void xfrHexBlob(string& val);
   void xfrBlob(string& val);
@@ -76,5 +76,5 @@
   void xfrType(const uint16_t& val);
   void xfrLabel(const string& val, bool compress=false);
-  void xfrText(const string& val);
+  void xfrText(const string& val, bool multi=false);
   void xfrBlob(const string& val);
   void xfrHexBlob(const string& val);
Index: dnsrecords.cc
===================================================================
--- dnsrecords.cc (revision 823)
+++ dnsrecords.cc (revision 996)
@@ -1,5 +1,5 @@
 /*
     PowerDNS Versatile Database Driven Nameserver
-    Copyright (C) 2005 - 2006  PowerDNS.COM BV
+    Copyright (C) 2005 - 2007  PowerDNS.COM BV
 
     This program is free software; you can redistribute it and/or modify
@@ -178,6 +178,6 @@
 boilerplate_conv(PTR, ns_t_ptr, conv.xfrLabel(d_content, true));
 boilerplate_conv(CNAME, ns_t_cname, conv.xfrLabel(d_content, true));
-boilerplate_conv(TXT, ns_t_txt, conv.xfrText(d_text));
-boilerplate_conv(SPF, 99, conv.xfrText(d_text));
+boilerplate_conv(TXT, ns_t_txt, conv.xfrText(d_text, true));
+boilerplate_conv(SPF, 99, conv.xfrText(d_text, true));
 boilerplate_conv(HINFO, ns_t_hinfo,  conv.xfrText(d_cpu);   conv.xfrText(d_host));
 
@@ -199,4 +199,9 @@
 		 conv.xfr16BitInt(d_preference);
 		 conv.xfrLabel(d_mxname, true);
+		 )
+
+boilerplate_conv(AFSDB, ns_t_afsdb, 
+		 conv.xfr16BitInt(d_subtype);
+		 conv.xfrLabel(d_hostname);
 		 )
 
@@ -235,4 +240,11 @@
 		 conv.xfr32BitInt(d_st.expire);
 		 conv.xfr32BitInt(d_st.minimum);
+		 );
+#undef KEY
+boilerplate_conv(KEY, ns_t_key, 
+		 conv.xfr16BitInt(d_flags); 
+		 conv.xfr8BitInt(d_protocol); 
+		 conv.xfr8BitInt(d_algorithm); 
+		 conv.xfrBlob(d_certificate);
 		 );
 
@@ -294,7 +306,9 @@
 void reportOtherTypes()
 {
+   AFSDBRecordContent::report();
    SPFRecordContent::report();
    NAPTRRecordContent::report();
    RPRecordContent::report();
+   KEYRecordContent::report();
    DNSKEYRecordContent::report();
    RRSIGRecordContent::report();
Index: dnsrecords.hh
===================================================================
--- dnsrecords.hh (revision 823)
+++ dnsrecords.hh (revision 978)
@@ -196,4 +196,26 @@
   string d_fingerprint;
 };
+
+class KEYRecordContent : public DNSRecordContent
+{
+public:
+  includeboilerplate(KEY)
+
+private:
+  uint16_t d_flags;
+  uint8_t d_protocol, d_algorithm;
+  string d_certificate;
+};
+
+class AFSDBRecordContent : public DNSRecordContent
+{
+public:
+  includeboilerplate(AFSDB)
+
+private:
+  uint16_t d_subtype;
+  string d_hostname;
+};
+
 
 class CERTRecordContent : public DNSRecordContent
Index: rcpgenerator.cc
===================================================================
--- rcpgenerator.cc (revision 850)
+++ rcpgenerator.cc (revision 996)
@@ -67,9 +67,38 @@
   if(!isdigit(d_string.at(d_pos)))
     throw RecordTextException("while parsing IP address, expected digits at position "+lexical_cast<string>(d_pos)+" in '"+d_string+"'");
-  
-  string ip;
-  xfrLabel(ip);
-  if(!IpToU32(ip, &val))
-    throw RecordTextException("unable to parse IP address '"+ip+"'");
+
+  uint32_t octet=0;
+  val=0;
+  char count=0;
+  
+  for(;;) {
+    if(d_string.at(d_pos)=='.') {
+      val<<=8;
+      val+=octet;
+      octet=0;
+      count++;
+      if(count > 3)
+	break;
+    }
+    else if(isdigit(d_string.at(d_pos))) {
+      octet*=10;
+      octet+=d_string.at(d_pos) - '0';
+      if(octet > 255)
+	throw RecordTextException("unable to parse IP address");
+    }
+    else if(dns_isspace(d_string.at(d_pos))) 
+      break;
+    else
+      throw RecordTextException("unable to parse IP address, strange character: "+d_string.at(d_pos));
+
+    d_pos++;
+    if(d_pos == d_string.length())
+      break;
+  }
+  if(count<=3) {
+    val<<=8;
+    val+=octet;
+  }
+  val=ntohl(val);
 }
 
@@ -178,23 +207,31 @@
 }
 
-
-void RecordTextReader::xfrText(string& val)
-{
-  skipSpaces();
-  if(d_string[d_pos]!='"')
-    throw RecordTextException("Data field in DNS should start with quote (\") at position "+lexical_cast<string>(d_pos)+" of '"+d_string+"'");
-
+void RecordTextReader::xfrText(string& val, bool multi)
+{
   val.clear();
   val.reserve(d_end - d_pos);
-  
-  while(++d_pos < d_end && d_string[d_pos]!='"') {
-    if(d_string[d_pos]=='\\' && d_pos+1!=d_end) {
-      ++d_pos;
+
+  while(d_pos != d_end) {
+    if(!val.empty())
+      val.append(1, ' ');
+
+    skipSpaces();
+    if(d_string[d_pos]!='"')
+      throw RecordTextException("Data field in DNS should start with quote (\") at position "+lexical_cast<string>(d_pos)+" of '"+d_string+"'");
+
+    val.append(1, '"');
+    while(++d_pos < d_end && d_string[d_pos]!='"') {
+      if(d_string[d_pos]=='\\' && d_pos+1!=d_end) {
+	val.append(1, d_string[d_pos++]);
+      }
+      val.append(1, d_string[d_pos]);
     }
-    val.append(1, d_string[d_pos]);
-  }
-  if(d_pos == d_end)
-    throw RecordTextException("Data field in DNS should end on a quote (\") in '"+d_string+"'");
-  d_pos++;
+    val.append(1,'"');
+    if(d_pos == d_end)
+      throw RecordTextException("Data field in DNS should end on a quote (\") in '"+d_string+"'");
+    d_pos++;
+    if(!multi)
+      break;
+  }
 }
 
@@ -251,11 +288,28 @@
 
   char tmp[17];
-  snprintf(tmp, sizeof(tmp)-1, "%u.%u.%u.%u", 
-	   (val >> 24)&0xff,
-	   (val >> 16)&0xff,
-	   (val >>  8)&0xff,
-	   (val      )&0xff);
-  
-  d_string+=tmp;
+  uint32_t ip=htonl(val);
+  uint8_t vals[4];
+
+  memcpy(&vals[0], &ip, sizeof(ip));
+
+  char *pos=tmp;
+
+  for(int n=0; n < 4; ++n) {
+    if(vals[n]<10) {
+      *(pos++)=vals[n]+'0';
+    } else if(vals[n] < 100) {
+      *(pos++)=(vals[n]/10) +'0';
+      *(pos++)=(vals[n]%10) +'0';
+    } else {
+      *(pos++)=(vals[n]/100) +'0';
+      vals[n]%=100;
+      *(pos++)=(vals[n]/10) +'0';
+      *(pos++)=(vals[n]%10) +'0';
+    }
+    if(n!=3)
+      *(pos++)='.';
+  }
+  *pos=0;
+  d_string.append(tmp, pos);
 }
 
@@ -338,23 +392,10 @@
 }
 
-void RecordTextWriter::xfrText(const string& val)
-{
-  if(!d_string.empty())
-    d_string.append(1,' ');
-  d_string.append(1,'"');
-
-  if(val.find_first_of("\\\"") == string::npos)
-    d_string+=val;
-  else {
-    string::size_type end=val.size();
-    
-    for(string::size_type pos=0; pos < end; ++pos) {
-      if(val[pos]=='\'' || val[pos]=='"')
-	d_string.append(1,'\\');
-      d_string.append(1, val[pos]);
-    }
-  }
-
-  d_string.append(1,'"');
+void RecordTextWriter::xfrText(const string& val, bool multi)
+{
+  if(!d_string.empty())
+    d_string.append(1,' ');
+
+  d_string.append(val);
 }
 
