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