]>
Commit | Line | Data |
---|---|---|
870a0fe4 AT |
1 | #ifdef HAVE_CONFIG_H |
2 | #include "config.h" | |
3 | #endif | |
a0a276c2 BH |
4 | #include "dnswriter.hh" |
5 | #include "misc.hh" | |
6 | #include "dnsparser.hh" | |
fa8fd4d2 | 7 | |
a6c51664 | 8 | #include <limits.h> |
a0a276c2 | 9 | |
c2f3be9d | 10 | DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint8_t opcode) |
df6147db | 11 | : d_pos(0), d_content(content), d_qname(qname), d_canonic(false), d_lowerCase(false) |
a0a276c2 BH |
12 | { |
13 | d_content.clear(); | |
14 | dnsheader dnsheader; | |
950cfe0f | 15 | |
a0a276c2 | 16 | memset(&dnsheader, 0, sizeof(dnsheader)); |
8e97e9a3 | 17 | dnsheader.id=0; |
a0a276c2 | 18 | dnsheader.qdcount=htons(1); |
88c1bc50 | 19 | dnsheader.opcode=opcode; |
950cfe0f | 20 | |
a0a276c2 | 21 | const uint8_t* ptr=(const uint8_t*)&dnsheader; |
790e7c1b BH |
22 | uint32_t len=d_content.size(); |
23 | d_content.resize(len + sizeof(dnsheader)); | |
24 | uint8_t* dptr=(&*d_content.begin()) + len; | |
950cfe0f | 25 | |
790e7c1b | 26 | memcpy(dptr, ptr, sizeof(dnsheader)); |
2f4c3abb | 27 | d_stuff=0; |
b59d34f7 | 28 | |
ad8fa726 | 29 | xfrName(qname, false); |
950cfe0f | 30 | |
790e7c1b | 31 | len=d_content.size(); |
678ce973 | 32 | d_content.resize(len + d_record.size() + 4); |
e984b465 | 33 | |
790e7c1b BH |
34 | ptr=&*d_record.begin(); |
35 | dptr=(&*d_content.begin()) + len; | |
950cfe0f | 36 | |
790e7c1b | 37 | memcpy(dptr, ptr, d_record.size()); |
790e7c1b | 38 | |
678ce973 | 39 | len+=d_record.size(); |
bca6643b BH |
40 | d_record.clear(); |
41 | ||
a0a276c2 | 42 | qtype=htons(qtype); |
a0a276c2 | 43 | qclass=htons(qclass); |
e984b465 BH |
44 | |
45 | vector<uint8_t>::iterator i=d_content.begin()+len; // this works around a gcc 3.4 bug | |
46 | memcpy(&*i, &qtype, 2); | |
47 | i+=2; | |
48 | memcpy(&*i, &qclass, 2); | |
ea634573 BH |
49 | |
50 | d_stuff=0xffff; | |
a2ce25e4 | 51 | d_labelmap.reserve(16); |
add935a2 | 52 | d_truncatemarker=d_content.size(); |
0eabbd4b AT |
53 | d_sor = 0; |
54 | d_rollbackmarker = 0; | |
55 | d_recordttl = 0; | |
56 | d_recordqtype = 0; | |
57 | d_recordqclass = QClass::IN; | |
e693ff5a | 58 | d_recordplace = DNSResourceRecord::ANSWER; |
a0a276c2 BH |
59 | } |
60 | ||
5a57d2ea | 61 | dnsheader* DNSPacketWriter::getHeader() |
8e97e9a3 | 62 | { |
b346905c | 63 | return reinterpret_cast<dnsheader*>(&*d_content.begin()); |
8e97e9a3 BH |
64 | } |
65 | ||
e693ff5a | 66 | void DNSPacketWriter::startRecord(const DNSName& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, DNSResourceRecord::Place place, bool compress) |
a0a276c2 | 67 | { |
950cfe0f | 68 | if(!d_record.empty()) |
a0a276c2 | 69 | commit(); |
878435ce | 70 | |
a0a276c2 BH |
71 | d_recordqname=name; |
72 | d_recordqtype=qtype; | |
73 | d_recordqclass=qclass; | |
878435ce | 74 | d_recordttl=ttl; |
10321a98 | 75 | d_recordplace=place; |
878435ce | 76 | |
f4d26b4f | 77 | d_stuff = 0; |
10321a98 | 78 | d_rollbackmarker=d_content.size(); |
bca6643b | 79 | |
c2f3be9d | 80 | if(compress && d_recordqname.countLabels() && d_qname==d_recordqname) { // don't do the whole label compression thing if we *know* we can get away with "see question" - except when compressing the root |
6f724e8b PD |
81 | static unsigned char marker[2]={0xc0, 0x0c}; |
82 | d_content.insert(d_content.end(), (const char *) &marker[0], (const char *) &marker[2]); | |
a2ce25e4 BH |
83 | } |
84 | else { | |
ad8fa726 | 85 | xfrName(d_recordqname, compress); |
a2ce25e4 BH |
86 | d_content.insert(d_content.end(), d_record.begin(), d_record.end()); |
87 | d_record.clear(); | |
88 | } | |
f4d26b4f | 89 | |
bca6643b | 90 | d_stuff = sizeof(dnsrecordheader); // this is needed to get compressed label offsets right, the dnsrecordheader will be interspersed |
950cfe0f | 91 | d_sor=d_content.size() + d_stuff; // start of real record |
878435ce | 92 | } |
a0a276c2 | 93 | |
7f7b8d55 | 94 | void DNSPacketWriter::addOpt(int udpsize, int extRCode, int Z, const vector<pair<uint16_t,string> >& options) |
878435ce BH |
95 | { |
96 | uint32_t ttl=0; | |
705f31ae | 97 | |
009f9f55 | 98 | EDNS0Record stuff; |
878435ce BH |
99 | |
100 | stuff.extRCode=extRCode; | |
101 | stuff.version=0; | |
102 | stuff.Z=htons(Z); | |
7f7b8d55 | 103 | |
878435ce | 104 | memcpy(&ttl, &stuff, sizeof(stuff)); |
8a63d3ce | 105 | |
878435ce | 106 | ttl=ntohl(ttl); // will be reversed later on |
950cfe0f | 107 | |
e693ff5a | 108 | startRecord(DNSName(), QType::OPT, ttl, udpsize, DNSResourceRecord::ADDITIONAL, false); |
7f7b8d55 BH |
109 | for(optvect_t::const_iterator iter = options.begin(); iter != options.end(); ++iter) { |
110 | xfr16BitInt(iter->first); | |
111 | xfr16BitInt(iter->second.length()); | |
112 | xfrBlob(iter->second); | |
950cfe0f | 113 | } |
a0a276c2 BH |
114 | } |
115 | ||
341930bb BH |
116 | void DNSPacketWriter::xfr48BitInt(uint64_t val) |
117 | { | |
118 | unsigned char bytes[6]; | |
a6e93c0f | 119 | uint16_t theLeft = htons((val >> 32)&0xffffU); |
0407751c BH |
120 | uint32_t theRight = htonl(val & 0xffffffffU); |
121 | memcpy(bytes, (void*)&theLeft, 2); | |
122 | memcpy(bytes+2, (void*)&theRight, 4); | |
341930bb BH |
123 | |
124 | d_record.insert(d_record.end(), bytes, bytes + 6); | |
125 | } | |
126 | ||
127 | ||
a0a276c2 BH |
128 | void DNSPacketWriter::xfr32BitInt(uint32_t val) |
129 | { | |
ea634573 BH |
130 | int rval=htonl(val); |
131 | uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval); | |
a0a276c2 BH |
132 | d_record.insert(d_record.end(), ptr, ptr+4); |
133 | } | |
134 | ||
135 | void DNSPacketWriter::xfr16BitInt(uint16_t val) | |
136 | { | |
96aed220 | 137 | uint16_t rval=htons(val); |
ea634573 | 138 | uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval); |
a0a276c2 BH |
139 | d_record.insert(d_record.end(), ptr, ptr+2); |
140 | } | |
141 | ||
142 | void DNSPacketWriter::xfr8BitInt(uint8_t val) | |
143 | { | |
144 | d_record.push_back(val); | |
145 | } | |
146 | ||
bac8f21b | 147 | |
950cfe0f | 148 | /* input: |
bac8f21b BH |
149 | "" -> 0 |
150 | "blah" -> 4blah | |
151 | "blah" "blah" -> output 4blah4blah | |
152 | "verylongstringlongerthan256....characters" \xffverylongstring\x23characters (autosplit) | |
153 | "blah\"blah" -> 9blah"blah | |
154 | "blah\97" -> 5blahb | |
155 | */ | |
ef6a78d5 | 156 | void DNSPacketWriter::xfrText(const string& text, bool) |
a0a276c2 | 157 | { |
bac8f21b | 158 | if(text.empty()) { |
ef6a78d5 | 159 | d_record.push_back(0); |
bac8f21b BH |
160 | return; |
161 | } | |
162 | vector<string> segments = segmentDNSText(text); | |
ef7cd021 | 163 | for(const string& str : segments) { |
bac8f21b BH |
164 | d_record.push_back(str.length()); |
165 | d_record.insert(d_record.end(), str.c_str(), str.c_str() + str.length()); | |
ef6a78d5 | 166 | } |
a0a276c2 BH |
167 | } |
168 | ||
950cfe0f | 169 | /* FIXME400: check that this beats a map */ |
0b6fa0eb | 170 | DNSPacketWriter::lmap_t::iterator find(DNSPacketWriter::lmap_t& nmap, const DNSName& name) |
a2ce25e4 BH |
171 | { |
172 | DNSPacketWriter::lmap_t::iterator ret; | |
950cfe0f | 173 | for(ret=nmap.begin(); ret != nmap.end(); ++ret) |
c07e01a8 | 174 | if(ret->first == name) |
a2ce25e4 BH |
175 | break; |
176 | return ret; | |
177 | } | |
178 | ||
c2f3be9d PD |
179 | // //! tokenize a label into parts, the parts describe a begin offset and an end offset |
180 | // bool labeltokUnescape(labelparts_t& parts, const string& label) | |
181 | // { | |
182 | // string::size_type epos = label.size(), lpos(0), pos; | |
183 | // bool unescapedSomething = false; | |
184 | // const char* ptr=label.c_str(); | |
185 | ||
186 | // parts.clear(); | |
187 | ||
188 | // for(pos = 0 ; pos < epos; ++pos) { | |
189 | // if(ptr[pos]=='\\') { | |
190 | // pos++; | |
191 | // unescapedSomething = true; | |
192 | // continue; | |
193 | // } | |
194 | // if(ptr[pos]=='.') { | |
195 | // parts.push_back(make_pair(lpos, pos)); | |
196 | // lpos=pos+1; | |
197 | // } | |
198 | // } | |
950cfe0f | 199 | |
c2f3be9d PD |
200 | // if(lpos < pos) |
201 | // parts.push_back(make_pair(lpos, pos)); | |
202 | // return unescapedSomething; | |
203 | // } | |
38e655b6 | 204 | |
950cfe0f | 205 | // this is the absolute hottest function in the pdns recursor |
c2f3be9d | 206 | void DNSPacketWriter::xfrName(const DNSName& name, bool compress) |
38e655b6 | 207 | { |
66ddf73d | 208 | //cerr<<"xfrName: name=["<<name.toString()<<"] compress="<<compress<<endl; |
c2f3be9d | 209 | // string label = d_lowerCase ? toLower(Label) : Label; |
3343ad1f | 210 | // FIXME400: we ignore d_lowerCase for now |
66ddf73d | 211 | // cerr<<"xfrName writing ["<<name.toString()<<"]"<<endl; |
c2f3be9d PD |
212 | std::vector<std::string> parts = name.getRawLabels(); |
213 | // labelparts_t parts; | |
66ddf73d | 214 | // cerr<<"labelcount: "<<parts.size()<<endl; |
c1d02c0d | 215 | |
f3f4938f BH |
216 | if(d_canonic) |
217 | compress=false; | |
218 | ||
c2f3be9d | 219 | if(!parts.size()) { // otherwise we encode '..' |
c1d02c0d BH |
220 | d_record.push_back(0); |
221 | return; | |
222 | } | |
950cfe0f | 223 | |
e5bad90b | 224 | // d_stuff is amount of stuff that is yet to be written out - the dnsrecordheader for example |
950cfe0f | 225 | unsigned int pos=d_content.size() + d_record.size() + d_stuff; |
c2f3be9d | 226 | // bool deDot = labellen && (label[labellen-1]=='.'); // make sure we don't store trailing dots in the labelmap |
f3f4938f | 227 | |
5be843df KM |
228 | unsigned int startRecordSize=d_record.size(); |
229 | unsigned int startPos; | |
230 | ||
950cfe0f PD |
231 | DNSName towrite = name; |
232 | /* FIXME400: if we are not compressing, there is no reason to work per-label */ | |
c2f3be9d | 233 | for(auto &label: parts) { |
5f3eca86 | 234 | if(d_lowerCase) label=toLower(label); |
66ddf73d | 235 | //cerr<<"xfrName labelpart ["<<label<<"], left to write ["<<towrite.toString()<<"]"<<endl; |
f3f4938f | 236 | |
0b6fa0eb | 237 | auto li=d_labelmap.end(); |
8c3149f2 | 238 | // see if we've written out this domain before |
0b6fa0eb KM |
239 | //cerr<<"compress="<<compress<<", searching? for compression pointer to '"<<towrite.toString()<<"', "<<d_labelmap.size()<<" cmp-records"<<endl; |
240 | if(compress && (li=find(d_labelmap, towrite))!=d_labelmap.end()) { | |
66ddf73d KM |
241 | //cerr<<"doing compression, my label=["<<label<<"] found match ["<<li->first.toString()<<"]"<<endl; |
242 | //cerr<<"\tFound a compression pointer to '"<<towrite.toString()<<"': "<<li->second<<endl; | |
950cfe0f | 243 | if (d_record.size() - startRecordSize + label.size() > 253) // chopped does not include a length octet for the first label and the root label |
ad8fa726 | 244 | throw MOADNSException("DNSPacketWriter::xfrName() found overly large (compressed) name"); |
bca6643b BH |
245 | uint16_t offset=li->second; |
246 | offset|=0xc000; | |
8c3149f2 BH |
247 | d_record.push_back((char)(offset >> 8)); |
248 | d_record.push_back((char)(offset & 0xff)); | |
bca6643b BH |
249 | goto out; // skip trailing 0 in case of compression |
250 | } | |
2f4c3abb | 251 | |
f3f4938f BH |
252 | if(li==d_labelmap.end() && pos< 16384) { |
253 | // cerr<<"\tStoring a compression pointer to '"<<chopped<<"': "<<pos<<endl; | |
0b6fa0eb | 254 | d_labelmap.push_back(make_pair(towrite, pos)); // if untrue, we need to count - also, don't store offsets > 16384, won't work |
66ddf73d | 255 | //cerr<<"stored ["<<towrite.toString()<<"] at pos "<<pos<<endl; |
f3f4938f | 256 | } |
a2ce25e4 | 257 | |
5be843df KM |
258 | startPos=pos; |
259 | ||
950cfe0f | 260 | char labelsize=label.size(); |
66ddf73d | 261 | //cerr<<"labelsize = "<<int(labelsize)<<" for label ["<<label<<"]"<<endl; |
950cfe0f PD |
262 | d_record.push_back(labelsize); |
263 | unsigned int len=d_record.size(); | |
264 | d_record.resize(len + labelsize); | |
3343ad1f | 265 | memcpy(((&*d_record.begin()) + len), label.c_str(), labelsize); // FIXME400 do not want memcpy |
950cfe0f | 266 | pos+=labelsize+1; |
5be843df KM |
267 | |
268 | if(pos - startPos == 1) | |
ad8fa726 | 269 | throw MOADNSException("DNSPacketWriter::xfrName() found empty label in the middle of name"); |
5be843df | 270 | if(pos - startPos > 64) |
ad8fa726 | 271 | throw MOADNSException("DNSPacketWriter::xfrName() found overly large label in name"); |
950cfe0f | 272 | towrite.chopOff(); /* FIXME400: iterating the label vector while keeping this chopoff in sync is a hack */ |
bca6643b | 273 | } |
5be843df KM |
274 | d_record.push_back(0); // insert root label |
275 | ||
276 | if (d_record.size() - startRecordSize > 255) | |
ad8fa726 | 277 | throw MOADNSException("DNSPacketWriter::xfrName() found overly large name"); |
a0a276c2 | 278 | |
950cfe0f | 279 | out:; |
a0a276c2 BH |
280 | } |
281 | ||
06ffdc52 | 282 | void DNSPacketWriter::xfrBlob(const string& blob, int ) |
8c1c9170 BH |
283 | { |
284 | const uint8_t* ptr=reinterpret_cast<const uint8_t*>(blob.c_str()); | |
8c1c9170 BH |
285 | d_record.insert(d_record.end(), ptr, ptr+blob.size()); |
286 | } | |
287 | ||
2fe9d6f7 AT |
288 | void DNSPacketWriter::xfrBlobNoSpaces(const string& blob, int ) |
289 | { | |
290 | xfrBlob(blob); | |
291 | } | |
292 | ||
e4090157 | 293 | void DNSPacketWriter::xfrHexBlob(const string& blob, bool keepReading) |
59a0f653 BH |
294 | { |
295 | xfrBlob(blob); | |
296 | } | |
297 | ||
ea634573 BH |
298 | void DNSPacketWriter::getRecords(string& records) |
299 | { | |
300 | records.assign(d_content.begin() + d_sor, d_content.end()); | |
301 | } | |
a0a276c2 | 302 | |
dffbaa08 | 303 | uint32_t DNSPacketWriter::size() |
10321a98 BH |
304 | { |
305 | return d_content.size() + d_stuff + d_record.size(); | |
306 | } | |
307 | ||
308 | void DNSPacketWriter::rollback() | |
309 | { | |
310 | d_content.resize(d_rollbackmarker); | |
311 | d_record.clear(); | |
312 | d_stuff=0; | |
313 | } | |
314 | ||
add935a2 PD |
315 | void DNSPacketWriter::truncate() |
316 | { | |
317 | d_content.resize(d_truncatemarker); | |
318 | d_record.clear(); | |
319 | d_stuff=0; | |
320 | dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin()); | |
321 | dh->ancount = dh->nscount = dh->arcount = 0; | |
322 | } | |
323 | ||
a0a276c2 BH |
324 | void DNSPacketWriter::commit() |
325 | { | |
ea634573 BH |
326 | if(d_stuff==0xffff && (d_content.size()!=d_sor || !d_record.empty())) |
327 | throw MOADNSException("DNSPacketWriter::commit() called without startRecord ever having been called, but a record was added"); | |
bca6643b | 328 | // build dnsrecordheader |
a0a276c2 BH |
329 | struct dnsrecordheader drh; |
330 | drh.d_type=htons(d_recordqtype); | |
331 | drh.d_class=htons(d_recordqclass); | |
878435ce | 332 | drh.d_ttl=htonl(d_recordttl); |
a0a276c2 | 333 | drh.d_clen=htons(d_record.size()); |
950cfe0f | 334 | |
bca6643b BH |
335 | // and write out the header |
336 | const uint8_t* ptr=(const uint8_t*)&drh; | |
a0a276c2 BH |
337 | d_content.insert(d_content.end(), ptr, ptr+sizeof(drh)); |
338 | ||
10321a98 BH |
339 | d_stuff=0; |
340 | ||
6f8b3628 | 341 | // write out pending d_record |
a0a276c2 BH |
342 | d_content.insert(d_content.end(), d_record.begin(), d_record.end()); |
343 | ||
10321a98 BH |
344 | dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin()); |
345 | switch(d_recordplace) { | |
e693ff5a AT |
346 | case DNSResourceRecord::QUESTION: |
347 | dh->qdcount = htons(ntohs(dh->qdcount) + 1); | |
348 | break; | |
349 | case DNSResourceRecord::ANSWER: | |
10321a98 BH |
350 | dh->ancount = htons(ntohs(dh->ancount) + 1); |
351 | break; | |
e693ff5a | 352 | case DNSResourceRecord::AUTHORITY: |
10321a98 BH |
353 | dh->nscount = htons(ntohs(dh->nscount) + 1); |
354 | break; | |
e693ff5a | 355 | case DNSResourceRecord::ADDITIONAL: |
10321a98 BH |
356 | dh->arcount = htons(ntohs(dh->arcount) + 1); |
357 | break; | |
358 | } | |
359 | ||
bca6643b | 360 | d_record.clear(); // clear d_record, ready for next record |
a0a276c2 | 361 | } |