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