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