| 1 | #include "dnswriter.hh" |
|---|
| 2 | #include "misc.hh" |
|---|
| 3 | #include "dnsparser.hh" |
|---|
| 4 | |
|---|
| 5 | DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const string& qname, uint16_t qtype, uint16_t qclass) |
|---|
| 6 | : d_pos(0), d_content(content), d_qname(qname), d_qtype(qtype), d_qclass(qclass) |
|---|
| 7 | { |
|---|
| 8 | d_content.clear(); |
|---|
| 9 | dnsheader dnsheader; |
|---|
| 10 | |
|---|
| 11 | memset(&dnsheader, 0, sizeof(dnsheader)); |
|---|
| 12 | dnsheader.id=0; |
|---|
| 13 | dnsheader.qdcount=htons(1); |
|---|
| 14 | |
|---|
| 15 | const uint8_t* ptr=(const uint8_t*)&dnsheader; |
|---|
| 16 | uint32_t len=d_content.size(); |
|---|
| 17 | d_content.resize(len + sizeof(dnsheader)); |
|---|
| 18 | uint8_t* dptr=(&*d_content.begin()) + len; |
|---|
| 19 | |
|---|
| 20 | memcpy(dptr, ptr, sizeof(dnsheader)); |
|---|
| 21 | d_stuff=0; |
|---|
| 22 | xfrLabel(qname, false); |
|---|
| 23 | |
|---|
| 24 | len=d_content.size(); |
|---|
| 25 | d_content.resize(len + d_record.size() + 4); |
|---|
| 26 | |
|---|
| 27 | ptr=&*d_record.begin(); |
|---|
| 28 | dptr=(&*d_content.begin()) + len; |
|---|
| 29 | |
|---|
| 30 | memcpy(dptr, ptr, d_record.size()); |
|---|
| 31 | |
|---|
| 32 | len+=d_record.size(); |
|---|
| 33 | d_record.clear(); |
|---|
| 34 | |
|---|
| 35 | qtype=htons(qtype); |
|---|
| 36 | qclass=htons(qclass); |
|---|
| 37 | |
|---|
| 38 | vector<uint8_t>::iterator i=d_content.begin()+len; // this works around a gcc 3.4 bug |
|---|
| 39 | memcpy(&*i, &qtype, 2); |
|---|
| 40 | i+=2; |
|---|
| 41 | memcpy(&*i, &qclass, 2); |
|---|
| 42 | |
|---|
| 43 | d_stuff=0xffff; |
|---|
| 44 | } |
|---|
| 45 | |
|---|
| 46 | dnsheader* DNSPacketWriter::getHeader() |
|---|
| 47 | { |
|---|
| 48 | return (dnsheader*)&*d_content.begin(); |
|---|
| 49 | } |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | void DNSPacketWriter::startRecord(const string& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, Place place) |
|---|
| 53 | { |
|---|
| 54 | if(!d_record.empty()) |
|---|
| 55 | commit(); |
|---|
| 56 | |
|---|
| 57 | d_recordqname=name; |
|---|
| 58 | d_recordqtype=qtype; |
|---|
| 59 | d_recordqclass=qclass; |
|---|
| 60 | d_recordttl=ttl; |
|---|
| 61 | d_recordplace=place; |
|---|
| 62 | |
|---|
| 63 | d_stuff = 0; |
|---|
| 64 | d_rollbackmarker=d_content.size(); |
|---|
| 65 | |
|---|
| 66 | xfrLabel(d_recordqname, true); |
|---|
| 67 | d_content.insert(d_content.end(), d_record.begin(), d_record.end()); |
|---|
| 68 | d_record.clear(); |
|---|
| 69 | |
|---|
| 70 | d_stuff = sizeof(dnsrecordheader); // this is needed to get compressed label offsets right, the dnsrecordheader will be interspersed |
|---|
| 71 | d_sor=d_content.size() + d_stuff; // start of real record |
|---|
| 72 | } |
|---|
| 73 | |
|---|
| 74 | void DNSPacketWriter::addOpt(int udpsize, int extRCode, int Z) |
|---|
| 75 | { |
|---|
| 76 | uint32_t ttl=0; |
|---|
| 77 | |
|---|
| 78 | EDNS0Record stuff; |
|---|
| 79 | |
|---|
| 80 | stuff.extRCode=extRCode; |
|---|
| 81 | stuff.version=0; |
|---|
| 82 | stuff.Z=htons(Z); |
|---|
| 83 | |
|---|
| 84 | memcpy(&ttl, &stuff, sizeof(stuff)); |
|---|
| 85 | |
|---|
| 86 | ttl=ntohl(ttl); // will be reversed later on |
|---|
| 87 | |
|---|
| 88 | startRecord("", ns_t_opt, ttl, udpsize, ADDITIONAL); |
|---|
| 89 | } |
|---|
| 90 | |
|---|
| 91 | void DNSPacketWriter::xfr32BitInt(uint32_t val) |
|---|
| 92 | { |
|---|
| 93 | int rval=htonl(val); |
|---|
| 94 | uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval); |
|---|
| 95 | d_record.insert(d_record.end(), ptr, ptr+4); |
|---|
| 96 | } |
|---|
| 97 | |
|---|
| 98 | void DNSPacketWriter::xfr16BitInt(uint16_t val) |
|---|
| 99 | { |
|---|
| 100 | uint16_t rval=htons(val); |
|---|
| 101 | uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval); |
|---|
| 102 | d_record.insert(d_record.end(), ptr, ptr+2); |
|---|
| 103 | } |
|---|
| 104 | |
|---|
| 105 | void DNSPacketWriter::xfr8BitInt(uint8_t val) |
|---|
| 106 | { |
|---|
| 107 | d_record.push_back(val); |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | void DNSPacketWriter::xfrText(const string& text) |
|---|
| 111 | { |
|---|
| 112 | d_record.push_back(text.length()); |
|---|
| 113 | const uint8_t* ptr=(uint8_t*)(text.c_str()); |
|---|
| 114 | d_record.insert(d_record.end(), ptr, ptr+text.size()); |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | // this is the absolute hottest function in the pdns recursor |
|---|
| 118 | void DNSPacketWriter::xfrLabel(const string& label, bool compress) |
|---|
| 119 | { |
|---|
| 120 | typedef vector<pair<unsigned int, unsigned int> > parts_t; |
|---|
| 121 | parts_t parts; |
|---|
| 122 | vstringtok(parts, label, "."); // XXX FIXME this should deal with escaped . |
|---|
| 123 | |
|---|
| 124 | // d_stuff is amount of stuff that is yet to be written out - the dnsrecordheader for example |
|---|
| 125 | unsigned int pos=d_content.size() + d_record.size() + d_stuff; |
|---|
| 126 | string chopped(label); |
|---|
| 127 | |
|---|
| 128 | for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) { |
|---|
| 129 | // cerr<<"chopped: '"<<chopped<<"'\n"; |
|---|
| 130 | map<string, uint16_t>::iterator li=d_labelmap.end(); |
|---|
| 131 | // see if we've written out this domain before |
|---|
| 132 | if(compress && (li=d_labelmap.find(chopped))!=d_labelmap.end()) { |
|---|
| 133 | uint16_t offset=li->second; |
|---|
| 134 | offset|=0xc000; |
|---|
| 135 | d_record.push_back((char)(offset >> 8)); |
|---|
| 136 | d_record.push_back((char)(offset & 0xff)); |
|---|
| 137 | goto out; // skip trailing 0 in case of compression |
|---|
| 138 | } |
|---|
| 139 | |
|---|
| 140 | if(li==d_labelmap.end() && pos< 16384) |
|---|
| 141 | d_labelmap[chopped]=pos; // if untrue, we need to count - also, don't store offsets > 16384, won't work |
|---|
| 142 | |
|---|
| 143 | d_record.push_back((char)(i->second - i->first)); |
|---|
| 144 | unsigned int len=d_record.size(); |
|---|
| 145 | d_record.resize(len + i->second - i->first); |
|---|
| 146 | memcpy(((&*d_record.begin()) + len), label.c_str() + i-> first, i->second - i->first); |
|---|
| 147 | // cerr<<"Added: '"<<string(label.c_str() + i->first, i->second - i->first) <<"'\n"; |
|---|
| 148 | pos+=(i->second - i->first)+1; |
|---|
| 149 | chopOff(chopped); // www.powerdns.com. -> powerdns.com. -> com. -> . |
|---|
| 150 | } |
|---|
| 151 | d_record.push_back(0); |
|---|
| 152 | |
|---|
| 153 | out:; |
|---|
| 154 | } |
|---|
| 155 | |
|---|
| 156 | void DNSPacketWriter::xfrBlob(const string& blob) |
|---|
| 157 | { |
|---|
| 158 | const uint8_t* ptr=reinterpret_cast<const uint8_t*>(blob.c_str()); |
|---|
| 159 | |
|---|
| 160 | d_record.insert(d_record.end(), ptr, ptr+blob.size()); |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | void DNSPacketWriter::xfrHexBlob(const string& blob) |
|---|
| 164 | { |
|---|
| 165 | xfrBlob(blob); |
|---|
| 166 | } |
|---|
| 167 | |
|---|
| 168 | |
|---|
| 169 | void DNSPacketWriter::getRecords(string& records) |
|---|
| 170 | { |
|---|
| 171 | records.assign(d_content.begin() + d_sor, d_content.end()); |
|---|
| 172 | } |
|---|
| 173 | |
|---|
| 174 | uint16_t DNSPacketWriter::size() |
|---|
| 175 | { |
|---|
| 176 | return d_content.size() + d_stuff + d_record.size(); |
|---|
| 177 | } |
|---|
| 178 | |
|---|
| 179 | void DNSPacketWriter::rollback() |
|---|
| 180 | { |
|---|
| 181 | d_content.resize(d_rollbackmarker); |
|---|
| 182 | d_record.clear(); |
|---|
| 183 | d_stuff=0; |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | void DNSPacketWriter::commit() |
|---|
| 187 | { |
|---|
| 188 | if(d_stuff==0xffff && (d_content.size()!=d_sor || !d_record.empty())) |
|---|
| 189 | throw MOADNSException("DNSPacketWriter::commit() called without startRecord ever having been called, but a record was added"); |
|---|
| 190 | // build dnsrecordheader |
|---|
| 191 | struct dnsrecordheader drh; |
|---|
| 192 | drh.d_type=htons(d_recordqtype); |
|---|
| 193 | drh.d_class=htons(d_recordqclass); |
|---|
| 194 | drh.d_ttl=htonl(d_recordttl); |
|---|
| 195 | drh.d_clen=htons(d_record.size()); |
|---|
| 196 | |
|---|
| 197 | // and write out the header |
|---|
| 198 | const uint8_t* ptr=(const uint8_t*)&drh; |
|---|
| 199 | d_content.insert(d_content.end(), ptr, ptr+sizeof(drh)); |
|---|
| 200 | |
|---|
| 201 | d_stuff=0; |
|---|
| 202 | |
|---|
| 203 | // write out d_record |
|---|
| 204 | d_content.insert(d_content.end(), d_record.begin(), d_record.end()); |
|---|
| 205 | |
|---|
| 206 | dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin()); |
|---|
| 207 | switch(d_recordplace) { |
|---|
| 208 | case ANSWER: |
|---|
| 209 | dh->ancount = htons(ntohs(dh->ancount) + 1); |
|---|
| 210 | break; |
|---|
| 211 | case AUTHORITY: |
|---|
| 212 | dh->nscount = htons(ntohs(dh->nscount) + 1); |
|---|
| 213 | break; |
|---|
| 214 | case ADDITIONAL: |
|---|
| 215 | dh->arcount = htons(ntohs(dh->arcount) + 1); |
|---|
| 216 | break; |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | d_record.clear(); // clear d_record, ready for next record |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | |
|---|
| 223 | |
|---|
| 224 | |
|---|
| 225 | |
|---|
| 226 | |
|---|