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