]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnswriter.cc
properly set ra bit if needed, closing ticket 167 reported by Augie Schwer
[thirdparty/pdns.git] / pdns / dnswriter.cc
CommitLineData
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 7DNSPacketWriter::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 51dnsheader* DNSPacketWriter::getHeader()
8e97e9a3
BH
52{
53 return (dnsheader*)&*d_content.begin();
54}
55
878435ce 56void 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
84void 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
101void 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
115void 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
122void 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
129void DNSPacketWriter::xfr8BitInt(uint8_t val)
130{
131 d_record.push_back(val);
132}
133
ef6a78d5 134void 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
152DNSPacketWriter::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
161typedef vector<pair<string::size_type, string::size_type> > parts_t;
162
163bool 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 189void 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
241void 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
248void DNSPacketWriter::xfrHexBlob(const string& blob)
249{
250 xfrBlob(blob);
251}
252
253
ea634573
BH
254void DNSPacketWriter::getRecords(string& records)
255{
256 records.assign(d_content.begin() + d_sor, d_content.end());
257}
a0a276c2 258
10321a98
BH
259uint16_t DNSPacketWriter::size()
260{
261 return d_content.size() + d_stuff + d_record.size();
262}
263
264void DNSPacketWriter::rollback()
265{
266 d_content.resize(d_rollbackmarker);
267 d_record.clear();
268 d_stuff=0;
269}
270
a0a276c2
BH
271void 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