]>
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]; | |
110 | bytes[5] = val % 0xff; val /= 0xff; // untested code! XXX FIXME | |
111 | bytes[4] = val % 0xff; val /= 0xff; | |
112 | bytes[3] = val % 0xff; val /= 0xff; | |
113 | bytes[2] = val % 0xff; val /= 0xff; | |
114 | bytes[1] = val % 0xff; val /= 0xff; | |
115 | bytes[0] = val % 0xff; val /= 0xff; | |
116 | ||
117 | d_record.insert(d_record.end(), bytes, bytes + 6); | |
118 | } | |
119 | ||
120 | ||
a0a276c2 BH |
121 | void DNSPacketWriter::xfr32BitInt(uint32_t val) |
122 | { | |
ea634573 BH |
123 | int rval=htonl(val); |
124 | uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval); | |
a0a276c2 BH |
125 | d_record.insert(d_record.end(), ptr, ptr+4); |
126 | } | |
127 | ||
128 | void DNSPacketWriter::xfr16BitInt(uint16_t val) | |
129 | { | |
96aed220 | 130 | uint16_t rval=htons(val); |
ea634573 | 131 | uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval); |
a0a276c2 BH |
132 | d_record.insert(d_record.end(), ptr, ptr+2); |
133 | } | |
134 | ||
135 | void DNSPacketWriter::xfr8BitInt(uint8_t val) | |
136 | { | |
137 | d_record.push_back(val); | |
138 | } | |
139 | ||
ef6a78d5 | 140 | void DNSPacketWriter::xfrText(const string& text, bool) |
a0a276c2 | 141 | { |
ef6a78d5 BH |
142 | escaped_list_separator<char> sep('\\', ' ' , '"'); |
143 | tokenizer<escaped_list_separator<char> > tok(text, sep); | |
144 | ||
145 | tokenizer<escaped_list_separator<char> >::iterator beg=tok.begin(); | |
146 | ||
147 | if(beg==tok.end()) { | |
148 | d_record.push_back(0); | |
149 | } | |
150 | else | |
151 | for(; beg!=tok.end(); ++beg){ | |
a6c51664 BH |
152 | if(beg->empty()) |
153 | d_record.push_back(0); | |
154 | else | |
155 | for (unsigned int i = 0; i < beg->length(); i += 0xff){ | |
156 | d_record.push_back(min(0xffU, beg->length()-i)); | |
157 | const uint8_t* ptr=(uint8_t*)(beg->c_str()) + i; | |
158 | d_record.insert(d_record.end(), ptr, ptr+min(0xffU, beg->length()-i)); | |
159 | } | |
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) | |
16989b4c | 167 | if(!strcasecmp(ret->first.c_str() ,label.c_str())) |
a2ce25e4 BH |
168 | break; |
169 | return ret; | |
170 | } | |
171 | ||
38e655b6 BH |
172 | typedef vector<pair<string::size_type, string::size_type> > parts_t; |
173 | ||
174 | bool labeltokUnescape(parts_t& parts, const string& label) | |
175 | { | |
176 | string::size_type epos = label.size(), lpos(0), pos; | |
177 | bool unescapedSomething = false; | |
178 | const char* ptr=label.c_str(); | |
179 | ||
180 | parts.clear(); | |
181 | ||
182 | for(pos = 0 ; pos < epos; ++pos) { | |
183 | if(ptr[pos]=='\\') { | |
184 | pos++; | |
185 | unescapedSomething = true; | |
186 | continue; | |
187 | } | |
188 | if(ptr[pos]=='.') { | |
189 | parts.push_back(make_pair(lpos, pos)); | |
190 | lpos=pos+1; | |
191 | } | |
192 | } | |
193 | ||
194 | if(lpos < pos) | |
195 | parts.push_back(make_pair(lpos, pos)); | |
196 | return unescapedSomething; | |
197 | } | |
198 | ||
a2ce25e4 | 199 | // this is the absolute hottest function in the pdns recursor |
bca6643b | 200 | void DNSPacketWriter::xfrLabel(const string& label, bool compress) |
a0a276c2 | 201 | { |
bca6643b | 202 | parts_t parts; |
c1d02c0d BH |
203 | |
204 | if(label.size()==1 && label[0]=='.') { // otherwise we encode '..' | |
205 | d_record.push_back(0); | |
206 | return; | |
207 | } | |
208 | ||
38e655b6 | 209 | bool unescaped=labeltokUnescape(parts, label); |
bca6643b | 210 | |
e5bad90b BH |
211 | // d_stuff is amount of stuff that is yet to be written out - the dnsrecordheader for example |
212 | unsigned int pos=d_content.size() + d_record.size() + d_stuff; | |
38e655b6 | 213 | string chopped; |
bca6643b | 214 | for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) { |
38e655b6 | 215 | chopped.assign(label.c_str() + i->first); |
a2ce25e4 | 216 | lmap_t::iterator li=d_labelmap.end(); |
8c3149f2 | 217 | // see if we've written out this domain before |
a2ce25e4 | 218 | if(compress && (li=find(d_labelmap, chopped))!=d_labelmap.end()) { |
bca6643b BH |
219 | uint16_t offset=li->second; |
220 | offset|=0xc000; | |
8c3149f2 BH |
221 | d_record.push_back((char)(offset >> 8)); |
222 | d_record.push_back((char)(offset & 0xff)); | |
bca6643b BH |
223 | goto out; // skip trailing 0 in case of compression |
224 | } | |
2f4c3abb BH |
225 | |
226 | if(li==d_labelmap.end() && pos< 16384) | |
a2ce25e4 BH |
227 | d_labelmap.push_back(make_pair(chopped, pos)); // if untrue, we need to count - also, don't store offsets > 16384, won't work |
228 | ||
38e655b6 BH |
229 | if(unescaped) { |
230 | string part(label.c_str() + i -> first, i->second - i->first); | |
231 | replace_all(part, "\\.", "."); | |
232 | d_record.push_back(part.size()); | |
233 | unsigned int len=d_record.size(); | |
234 | d_record.resize(len + part.size()); | |
235 | ||
236 | memcpy(((&*d_record.begin()) + len), part.c_str(), part.size()); | |
237 | pos+=(part.size())+1; | |
238 | } | |
239 | else { | |
240 | d_record.push_back((char)(i->second - i->first)); | |
241 | unsigned int len=d_record.size(); | |
242 | d_record.resize(len + i->second - i->first); | |
243 | memcpy(((&*d_record.begin()) + len), label.c_str() + i-> first, i->second - i->first); | |
244 | pos+=(i->second - i->first)+1; | |
245 | } | |
bca6643b | 246 | } |
8c3149f2 | 247 | d_record.push_back(0); |
a0a276c2 | 248 | |
bca6643b | 249 | out:; |
a0a276c2 BH |
250 | } |
251 | ||
06ffdc52 | 252 | void DNSPacketWriter::xfrBlob(const string& blob, int ) |
8c1c9170 BH |
253 | { |
254 | const uint8_t* ptr=reinterpret_cast<const uint8_t*>(blob.c_str()); | |
255 | ||
256 | d_record.insert(d_record.end(), ptr, ptr+blob.size()); | |
257 | } | |
258 | ||
59a0f653 BH |
259 | void DNSPacketWriter::xfrHexBlob(const string& blob) |
260 | { | |
261 | xfrBlob(blob); | |
262 | } | |
263 | ||
264 | ||
ea634573 BH |
265 | void DNSPacketWriter::getRecords(string& records) |
266 | { | |
267 | records.assign(d_content.begin() + d_sor, d_content.end()); | |
268 | } | |
a0a276c2 | 269 | |
10321a98 BH |
270 | uint16_t DNSPacketWriter::size() |
271 | { | |
272 | return d_content.size() + d_stuff + d_record.size(); | |
273 | } | |
274 | ||
275 | void DNSPacketWriter::rollback() | |
276 | { | |
277 | d_content.resize(d_rollbackmarker); | |
278 | d_record.clear(); | |
279 | d_stuff=0; | |
280 | } | |
281 | ||
a0a276c2 BH |
282 | void DNSPacketWriter::commit() |
283 | { | |
ea634573 BH |
284 | if(d_stuff==0xffff && (d_content.size()!=d_sor || !d_record.empty())) |
285 | throw MOADNSException("DNSPacketWriter::commit() called without startRecord ever having been called, but a record was added"); | |
bca6643b | 286 | // build dnsrecordheader |
a0a276c2 BH |
287 | struct dnsrecordheader drh; |
288 | drh.d_type=htons(d_recordqtype); | |
289 | drh.d_class=htons(d_recordqclass); | |
878435ce | 290 | drh.d_ttl=htonl(d_recordttl); |
a0a276c2 | 291 | drh.d_clen=htons(d_record.size()); |
10321a98 | 292 | |
bca6643b BH |
293 | // and write out the header |
294 | const uint8_t* ptr=(const uint8_t*)&drh; | |
a0a276c2 BH |
295 | d_content.insert(d_content.end(), ptr, ptr+sizeof(drh)); |
296 | ||
10321a98 BH |
297 | d_stuff=0; |
298 | ||
6f8b3628 | 299 | // write out pending d_record |
a0a276c2 BH |
300 | d_content.insert(d_content.end(), d_record.begin(), d_record.end()); |
301 | ||
10321a98 BH |
302 | dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin()); |
303 | switch(d_recordplace) { | |
304 | case ANSWER: | |
305 | dh->ancount = htons(ntohs(dh->ancount) + 1); | |
306 | break; | |
307 | case AUTHORITY: | |
308 | dh->nscount = htons(ntohs(dh->nscount) + 1); | |
309 | break; | |
310 | case ADDITIONAL: | |
311 | dh->arcount = htons(ntohs(dh->arcount) + 1); | |
312 | break; | |
313 | } | |
314 | ||
bca6643b | 315 | d_record.clear(); // clear d_record, ready for next record |
a0a276c2 BH |
316 | } |
317 | ||
318 | ||
319 | ||
320 | ||
321 | ||
322 |