]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsname.cc
turns out we were using libc tolower in performance sensitive places.. top in perf
[thirdparty/pdns.git] / pdns / dnsname.cc
1 #include "dnsname.hh"
2 #include <boost/format.hpp>
3 #include <string>
4
5 #include "dnswriter.hh"
6 #include "misc.hh"
7
8 #include <boost/functional/hash.hpp>
9
10 /* raw storage
11 in DNS label format, with trailing 0. W/o trailing 0, we are 'empty'
12 www.powerdns.com = 3www8powerdns3com0
13 */
14
15 std::ostream & operator<<(std::ostream &os, const DNSName& d)
16 {
17 return os <<d.toString();
18 }
19
20
21 DNSName::DNSName(const char* p)
22 {
23 if(p[0]==0 || (p[0]=='.' && p[1]==0)) {
24 d_storage.assign(1, (char)0);
25 } else {
26 d_storage.reserve(strlen(p)+1);
27 auto labels = segmentDNSName(p);
28 for(const auto& e : labels)
29 appendRawLabel(e);
30 }
31 }
32
33 DNSName::DNSName(const char* pos, int len, int offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed)
34 {
35 if (offset >= len)
36 throw std::range_error("Trying to read past the end of the buffer");
37
38 if(!uncompress) {
39 if(const void * fnd=memchr(pos+offset, 0, len-offset)) {
40 d_storage.reserve(2+(const char*)fnd-(pos+offset));
41 }
42 }
43
44 packetParser(pos, len, offset, uncompress, qtype, qclass, consumed);
45 }
46
47 // this should be the __only__ dns name parser in PowerDNS.
48 void DNSName::packetParser(const char* qpos, int len, int offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth)
49 {
50 const unsigned char* pos=(const unsigned char*)qpos;
51 unsigned char labellen;
52 const unsigned char *opos = pos;
53
54 if (offset >= len)
55 throw std::range_error("Trying to read past the end of the buffer");
56
57 pos += offset;
58 const unsigned char* end = pos + len;
59 while((labellen=*pos++) && pos < end) { // "scan and copy"
60 if(labellen & 0xc0) {
61 if(!uncompress)
62 throw std::range_error("Found compressed label, instructed not to follow");
63
64 labellen &= (~0xc0);
65 int newpos = (labellen << 8) + *(const unsigned char*)pos;
66
67 if(newpos < offset) {
68 if (++depth > 100)
69 throw std::range_error("Abort label decompression after 100 redirects");
70 packetParser((const char*)opos, len, newpos, true, 0, 0, 0, depth);
71 } else
72 throw std::range_error("Found a forward reference during label decompression");
73 pos++;
74 break;
75 }
76 if (pos + labellen < end) {
77 appendRawLabel((const char*)pos, labellen);
78 }
79 else
80 throw std::range_error("Found an invalid label length in qname");
81 pos+=labellen;
82 }
83 if(d_storage.empty())
84 d_storage.append(1, (char)0); // we just parsed the root
85 if(consumed)
86 *consumed = pos - opos - offset;
87 if(qtype && pos + labellen + 2 <= end)
88 *qtype=(*(const unsigned char*)pos)*256 + *((const unsigned char*)pos+1);
89
90 pos+=2;
91 if(qclass && pos + labellen + 2 <= end)
92 *qclass=(*(const unsigned char*)pos)*256 + *((const unsigned char*)pos+1);
93
94 }
95
96 std::string DNSName::toString(const std::string& separator, const bool trailing) const
97 {
98 if (empty()) {
99 throw std::out_of_range("Attempt to print an unset dnsname");
100 }
101
102 if(isRoot())
103 return trailing ? separator : "";
104
105 std::string ret;
106 for(const auto& s : getRawLabels()) {
107 ret+= escapeLabel(s) + separator;
108 }
109
110 return ret.substr(0, ret.size()-!trailing);
111 }
112
113 std::string DNSName::toDNSString() const
114 {
115 if (empty())
116 throw std::out_of_range("Attempt to DNSString an unset dnsname");
117
118 return std::string(d_storage.c_str(), d_storage.length());
119 }
120
121 std::string DNSName::toDNSStringLC() const
122 {
123 return toLower(toDNSString()); // label lengths are always < 'A'
124 }
125
126 /**
127 * Get the length of the DNSName on the wire
128 *
129 * @return the total wirelength of the DNSName
130 */
131 size_t DNSName::wirelength() const {
132 return d_storage.length();
133 }
134
135 // Are WE part of parent
136 bool DNSName::isPartOf(const DNSName& parent) const
137 {
138 if(parent.empty() || empty())
139 throw std::out_of_range("empty dnsnames aren't part of anything");
140
141 if(parent.d_storage.size() > d_storage.size())
142 return false;
143
144 // this is slightly complicated since we can't start from the end, since we can't see where a label begins/ends then
145 for(auto us=d_storage.cbegin(); us<d_storage.cend() && std::distance(us,d_storage.cend()) >= static_cast<unsigned int>(parent.d_storage.size()); us+=*us+1) {
146 if (std::distance(us,d_storage.cend()) == static_cast<unsigned int>(parent.d_storage.size())) {
147 auto p = parent.d_storage.cbegin();
148 for(; us != d_storage.cend(); ++us, ++p) {
149 if(dns2_tolower(*p) != dns2_tolower(*us))
150 return false;
151 }
152 return true;
153 }
154 }
155 return false;
156 }
157
158 DNSName DNSName::makeRelative(const DNSName& zone) const
159 {
160 DNSName ret(*this);
161 ret.makeUsRelative(zone);
162 return ret.empty() ? zone : ret; // HACK FIXME400
163 }
164 void DNSName::makeUsRelative(const DNSName& zone)
165 {
166 if (isPartOf(zone)) {
167 d_storage.erase(d_storage.size()-zone.d_storage.size());
168 d_storage.append(1, (char)0); // put back the trailing 0
169 }
170 else
171 clear();
172 }
173
174 DNSName DNSName::labelReverse() const
175 {
176 DNSName ret;
177
178 if(isRoot())
179 return *this; // we don't create the root automatically below
180
181 if (!empty()) {
182 vector<string> l=getRawLabels();
183 while(!l.empty()) {
184 ret.appendRawLabel(l.back());
185 l.pop_back();
186 }
187 }
188 return ret;
189 }
190
191 void DNSName::appendRawLabel(const std::string& label)
192 {
193 appendRawLabel(label.c_str(), label.length());
194 }
195
196 void DNSName::appendRawLabel(const char* start, unsigned int length)
197 {
198 if(length==0)
199 throw std::range_error("no such thing as an empty label to append");
200 if(length > 63)
201 throw std::range_error("label too long to append");
202 if(d_storage.size() + length > 254) // reserve two bytes, one for length and one for the root label
203 throw std::range_error("name too long to append");
204
205 if(d_storage.empty()) {
206 d_storage.append(1, (char)length);
207 }
208 else {
209 *d_storage.rbegin()=(char)length;
210 }
211 d_storage.append(start, length);
212 d_storage.append(1, (char)0);
213 }
214
215 void DNSName::prependRawLabel(const std::string& label)
216 {
217 if(label.empty())
218 throw std::range_error("no such thing as an empty label to prepend");
219 if(label.size() > 63)
220 throw std::range_error("label too long to prepend");
221 if(d_storage.size() + label.size() > 254) // reserve two bytes, one for length and one for the root label
222 throw std::range_error("name too long to prepend");
223
224 if(d_storage.empty())
225 d_storage.append(1, (char)0);
226
227 string_t prep(1, (char)label.size());
228 prep.append(label.c_str(), label.size());
229 d_storage = prep+d_storage;
230 }
231
232 bool DNSName::slowCanonCompare(const DNSName& rhs) const
233 {
234 auto ours=getRawLabels(), rhsLabels = rhs.getRawLabels();
235 return std::lexicographical_compare(ours.rbegin(), ours.rend(), rhsLabels.rbegin(), rhsLabels.rend(), CIStringCompare());
236 }
237
238 vector<string> DNSName::getRawLabels() const
239 {
240 vector<string> ret;
241 ret.reserve(countLabels());
242 // 3www4ds9a2nl0
243 for(const char* p = d_storage.c_str(); p < d_storage.c_str() + d_storage.size() && *p; p+=*p+1)
244 ret.push_back({p+1, (unsigned int)*p}); // XXX FIXME
245 return ret;
246 }
247
248
249 bool DNSName::chopOff()
250 {
251 if(d_storage.empty() || d_storage[0]==0)
252 return false;
253 d_storage.erase(0, (unsigned int)d_storage[0]+1);
254 return true;
255 }
256
257 bool DNSName::isWildcard() const
258 {
259 if(d_storage.size() < 2)
260 return false;
261 auto p = d_storage.begin();
262 return (*p == 0x01 && *++p == '*');
263 }
264
265 unsigned int DNSName::countLabels() const
266 {
267 unsigned int count=0;
268 for(const char* p = d_storage.c_str(); p < d_storage.c_str() + d_storage.size() && *p; p+=*p+1)
269 ++count;
270 return count;
271 }
272
273 void DNSName::trimToLabels(unsigned int to)
274 {
275 while(countLabels() > to && chopOff())
276 ;
277 }
278
279 bool DNSName::operator==(const DNSName& rhs) const
280 {
281 if(rhs.empty() != empty() || rhs.d_storage.size() != d_storage.size())
282 return false;
283
284 auto us = d_storage.crbegin();
285 auto p = rhs.d_storage.crbegin();
286 for(; us != d_storage.crend() && p != rhs.d_storage.crend(); ++us, ++p) { // why does this go backward?
287 if(dns2_tolower(*p) != dns2_tolower(*us))
288 return false;
289 }
290 return true;
291 }
292
293 size_t hash_value(DNSName const& d)
294 {
295 return d.hash();
296 }
297
298 string DNSName::escapeLabel(const std::string& label)
299 {
300 string ret;
301 ret.reserve(label.size()); // saves 15% on bulk .COM load
302 for(uint8_t p : label) {
303 if(p=='.')
304 ret+="\\.";
305 else if(p=='\\')
306 ret+="\\\\";
307 else if(p > 0x21 && p < 0x7e)
308 ret.append(1, (char)p);
309 else {
310 ret+="\\" + (boost::format("%03d") % (unsigned int)p).str();
311 }
312 }
313 return ret;
314 }