]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsname.cc
Merge pull request #9134 from omoerbeek/secpoll-cleanup
[thirdparty/pdns.git] / pdns / dnsname.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
3c115e0f 22#include "dnsname.hh"
23#include <boost/format.hpp>
24#include <string>
27b19e8b 25#include <cinttypes>
6d8bc3c6 26
3c115e0f 27#include "dnswriter.hh"
5e1031cd
PD
28#include "misc.hh"
29
30#include <boost/functional/hash.hpp>
3c115e0f 31
c21484df 32const DNSName g_rootdnsname("."), g_wildcarddnsname("*");
33
97e5c6bc 34/* raw storage
ae14c1f3 35 in DNS label format, with trailing 0. W/o trailing 0, we are 'empty'
36 www.powerdns.com = 3www8powerdns3com0
97e5c6bc 37*/
38
8171ab83 39std::ostream & operator<<(std::ostream &os, const DNSName& d)
40{
39b3e0b2 41 return os <<d.toLogString();
8171ab83 42}
43
beb9fad5 44DNSName::DNSName(const char* p, size_t length)
3c115e0f 45{
6c7983ed 46 if(p[0]==0 || (p[0]=='.' && p[1]==0)) {
ae14c1f3 47 d_storage.assign(1, (char)0);
48 } else {
bae1b0a2 49 if(!strchr(p, '\\')) {
50 unsigned char lenpos=0;
51 unsigned char labellen=0;
beb9fad5 52 size_t plen=length;
bae1b0a2 53 const char* const pbegin=p, *pend=p+plen;
54 d_storage.reserve(plen+1);
55 for(auto iter = pbegin; iter != pend; ) {
56 lenpos = d_storage.size();
57 if(*iter=='.')
58 throw std::runtime_error("Found . in wrong position in DNSName "+string(p));
59 d_storage.append(1, (char)0);
60 labellen=0;
61 auto begiter=iter;
62 for(; iter != pend && *iter!='.'; ++iter) {
63 labellen++;
64 }
65 d_storage.append(begiter,iter);
66 if(iter != pend)
67 ++iter;
68 if(labellen > 63)
69 throw std::range_error("label too long to append");
70
71 if(iter-pbegin > 254) // reserve two bytes, one for length and one for the root label
72 throw std::range_error("name too long to append");
73
74 d_storage[lenpos]=labellen;
75 }
76 d_storage.append(1, (char)0);
77 }
d0a4a4bc 78 else {
beb9fad5 79 d_storage=segmentDNSNameRaw(p, length);
d0a4a4bc
RG
80 if(d_storage.size() > 255) {
81 throw std::range_error("name too long");
82 }
83 }
ae14c1f3 84 }
3c115e0f 85}
86
bae1b0a2 87
83fc9d8a 88DNSName::DNSName(const char* pos, int len, int offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, uint16_t minOffset)
3c115e0f 89{
adbace93 90 if (offset >= len)
e415d842 91 throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")");
adbace93 92
e14febcf 93 if(!uncompress) {
94 if(const void * fnd=memchr(pos+offset, 0, len-offset)) {
95 d_storage.reserve(2+(const char*)fnd-(pos+offset));
96 }
97 }
98
83fc9d8a 99 packetParser(pos, len, offset, uncompress, qtype, qclass, consumed, 0, minOffset);
ac7921b0 100}
101
102// this should be the __only__ dns name parser in PowerDNS.
83fc9d8a 103void DNSName::packetParser(const char* qpos, int len, int offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset)
ac7921b0 104{
9f652a9c 105 const unsigned char* pos=(const unsigned char*)qpos;
3c115e0f 106 unsigned char labellen;
9f652a9c 107 const unsigned char *opos = pos;
3bf1f0b0
RG
108
109 if (offset >= len)
e415d842 110 throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")");
83fc9d8a
RG
111 if (offset < (int) minOffset)
112 throw std::range_error("Trying to read before the beginning of the buffer ("+std::to_string(offset)+ " < "+std::to_string(minOffset)+")");
3bf1f0b0 113
9f652a9c 114 const unsigned char* end = pos + len;
b43f60f3 115 pos += offset;
3c115e0f 116 while((labellen=*pos++) && pos < end) { // "scan and copy"
99bbbc7b 117 if(labellen >= 0xc0) {
b5b1018e 118 if(!uncompress)
ac7921b0 119 throw std::range_error("Found compressed label, instructed not to follow");
b5b1018e 120
121 labellen &= (~0xc0);
122 int newpos = (labellen << 8) + *(const unsigned char*)pos;
123
9114819c 124 if(newpos < offset) {
83fc9d8a
RG
125 if(newpos < (int) minOffset)
126 throw std::range_error("Invalid label position during decompression ("+std::to_string(newpos)+ " < "+std::to_string(minOffset)+")");
4c6ce4f1 127 if (++depth > 100)
9114819c 128 throw std::range_error("Abort label decompression after 100 redirects");
83fc9d8a 129 packetParser((const char*)opos, len, newpos, true, 0, 0, 0, depth, minOffset);
9114819c 130 } else
30826379 131 throw std::range_error("Found a forward reference during label decompression");
b5b1018e 132 pos++;
133 break;
99bbbc7b
RG
134 } else if(labellen & 0xc0) {
135 throw std::range_error("Found an invalid label length in qname (only one of the first two bits is set)");
b5b1018e 136 }
ac7921b0 137 if (pos + labellen < end) {
e14febcf 138 appendRawLabel((const char*)pos, labellen);
ac7921b0 139 }
140 else
141 throw std::range_error("Found an invalid label length in qname");
3c115e0f 142 pos+=labellen;
143 }
f867aa74 144 if(d_storage.empty())
145 d_storage.append(1, (char)0); // we just parsed the root
520eb5a0 146 if(consumed)
147 *consumed = pos - opos - offset;
bd910269 148 if(qtype) {
7b9c052c
RG
149 if (pos + 2 > end) {
150 throw std::range_error("Trying to read qtype past the end of the buffer ("+std::to_string((pos - opos) + 2)+ " > "+std::to_string(len)+")");
bd910269 151 }
3c115e0f 152 *qtype=(*(const unsigned char*)pos)*256 + *((const unsigned char*)pos+1);
bd910269 153 }
31b386e1 154 pos+=2;
bd910269 155 if(qclass) {
7b9c052c
RG
156 if (pos + 2 > end) {
157 throw std::range_error("Trying to read qclass past the end of the buffer ("+std::to_string((pos - opos) + 2)+ " > "+std::to_string(len)+")");
bd910269 158 }
31b386e1 159 *qclass=(*(const unsigned char*)pos)*256 + *((const unsigned char*)pos+1);
bd910269 160 }
3c115e0f 161}
162
a61e8e59 163std::string DNSName::toString(const std::string& separator, const bool trailing) const
3c115e0f 164{
e1a9ab9f 165 if (empty()) {
ae14c1f3 166 throw std::out_of_range("Attempt to print an unset dnsname");
e1a9ab9f 167 }
ae14c1f3 168
f0da87e0 169 if(isRoot())
07338ade
KM
170 return trailing ? separator : "";
171
3c115e0f 172 std::string ret;
f0da87e0 173 ret.reserve(d_storage.size());
19164b93
CHB
174
175 {
176 // iterate over the raw labels
177 const char* p = d_storage.c_str();
178 const char* end = p + d_storage.size();
179
180 while (p < end && *p) {
2430e53c
RG
181 appendEscapedLabel(ret, p + 1, static_cast<size_t>(*p));
182 ret += separator;
19164b93
CHB
183 p += *p + 1;
184 }
3c115e0f 185 }
f0da87e0
CHB
186 if (!trailing) {
187 ret.resize(ret.size() - separator.size());
188 }
189 return ret;
3c115e0f 190}
191
9ab84270
PD
192std::string DNSName::toLogString() const
193{
194 if (empty()) {
195 return "(empty)";
196 }
197
a724f955 198 return toStringRootDot();
9ab84270
PD
199}
200
3c115e0f 201std::string DNSName::toDNSString() const
202{
ae14c1f3 203 if (empty())
204 throw std::out_of_range("Attempt to DNSString an unset dnsname");
205
0ba4e1ee 206 return std::string(d_storage.c_str(), d_storage.length());
207}
208
209std::string DNSName::toDNSStringLC() const
210{
211 return toLower(toDNSString()); // label lengths are always < 'A'
3c115e0f 212}
213
3155c04a
PL
214/**
215 * Get the length of the DNSName on the wire
216 *
217 * @return the total wirelength of the DNSName
218 */
219size_t DNSName::wirelength() const {
ae14c1f3 220 return d_storage.length();
3155c04a
PL
221}
222
ae14c1f3 223// Are WE part of parent
3c115e0f 224bool DNSName::isPartOf(const DNSName& parent) const
225{
d44e4e09 226 if(parent.empty() || empty())
ae14c1f3 227 throw std::out_of_range("empty dnsnames aren't part of anything");
228
9e98d926 229 if(parent.d_storage.size() > d_storage.size())
3bd38afa 230 return false;
231
232 // this is slightly complicated since we can't start from the end, since we can't see where a label begins/ends then
d8ec8f44
RG
233 for(auto us=d_storage.cbegin(); us<d_storage.cend(); us+=*us+1) {
234 auto distance = std::distance(us,d_storage.cend());
235 if (distance < 0 || static_cast<size_t>(distance) < parent.d_storage.size()) {
236 break;
237 }
238 if (static_cast<size_t>(distance) == parent.d_storage.size()) {
3bd38afa 239 auto p = parent.d_storage.cbegin();
9e98d926 240 for(; us != d_storage.cend(); ++us, ++p) {
2b62292d 241 if(dns_tolower(*p) != dns_tolower(*us))
9e98d926 242 return false;
3bd38afa 243 }
9e98d926 244 return true;
3bd38afa 245 }
c9742dfb
RG
246 if (*us < 0) {
247 throw std::out_of_range("negative label length in dnsname");
248 }
3c115e0f 249 }
3bd38afa 250 return false;
3c115e0f 251}
252
a61e8e59
KM
253DNSName DNSName::makeRelative(const DNSName& zone) const
254{
255 DNSName ret(*this);
8ca15224 256 ret.makeUsRelative(zone);
07338ade 257 return ret.empty() ? zone : ret; // HACK FIXME400
a61e8e59 258}
8ca15224 259void DNSName::makeUsRelative(const DNSName& zone)
260{
261 if (isPartOf(zone)) {
262 d_storage.erase(d_storage.size()-zone.d_storage.size());
ae14c1f3 263 d_storage.append(1, (char)0); // put back the trailing 0
8ca15224 264 }
265 else
266 clear();
267}
a61e8e59 268
9b061cf5
RG
269DNSName DNSName::getCommonLabels(const DNSName& other) const
270{
271 DNSName result;
272
273 const std::vector<std::string> ours = getRawLabels();
274 const std::vector<std::string> others = other.getRawLabels();
275
276 for (size_t pos = 0; ours.size() > pos && others.size() > pos; pos++) {
097f8a42
RG
277 const std::string& ourLabel = ours.at(ours.size() - pos - 1);
278 const std::string& otherLabel = others.at(others.size() - pos - 1);
279
280 if (!pdns_iequals(ourLabel, otherLabel)) {
9b061cf5
RG
281 break;
282 }
283
097f8a42 284 result.prependRawLabel(ourLabel);
9b061cf5
RG
285 }
286
287 return result;
288}
289
a61e8e59
KM
290DNSName DNSName::labelReverse() const
291{
292 DNSName ret;
c2b55ab9 293
e1a9ab9f 294 if(isRoot())
295 return *this; // we don't create the root automatically below
c2b55ab9 296
d44e4e09 297 if (!empty()) {
a61e8e59
KM
298 vector<string> l=getRawLabels();
299 while(!l.empty()) {
300 ret.appendRawLabel(l.back());
301 l.pop_back();
302 }
303 }
304 return ret;
305}
306
3c115e0f 307void DNSName::appendRawLabel(const std::string& label)
308{
e14febcf 309 appendRawLabel(label.c_str(), label.length());
310}
311
312void DNSName::appendRawLabel(const char* start, unsigned int length)
313{
314 if(length==0)
e11dc8c8 315 throw std::range_error("no such thing as an empty label to append");
e14febcf 316 if(length > 63)
e11dc8c8 317 throw std::range_error("label too long to append");
1106afd9 318 if(d_storage.size() + length > 254) // reserve one byte for the label length
e11dc8c8 319 throw std::range_error("name too long to append");
ac7921b0 320
ae14c1f3 321 if(d_storage.empty()) {
e14febcf 322 d_storage.append(1, (char)length);
ae14c1f3 323 }
324 else {
e14febcf 325 *d_storage.rbegin()=(char)length;
ae14c1f3 326 }
e14febcf 327 d_storage.append(start, length);
ae14c1f3 328 d_storage.append(1, (char)0);
3c115e0f 329}
330
331void DNSName::prependRawLabel(const std::string& label)
332{
564ec901 333 if(label.empty())
e11dc8c8 334 throw std::range_error("no such thing as an empty label to prepend");
be6fbc68 335 if(label.size() > 63)
e11dc8c8 336 throw std::range_error("label too long to prepend");
1106afd9 337 if(d_storage.size() + label.size() > 254) // reserve one byte for the label length
e11dc8c8 338 throw std::range_error("name too long to prepend");
be6fbc68 339
ae14c1f3 340 if(d_storage.empty())
341 d_storage.append(1, (char)0);
342
d8b778ff 343 string_t prep(1, (char)label.size());
344 prep.append(label.c_str(), label.size());
97e5c6bc 345 d_storage = prep+d_storage;
3c115e0f 346}
347
ddb7e6c6 348bool DNSName::slowCanonCompare(const DNSName& rhs) const
349{
350 auto ours=getRawLabels(), rhsLabels = rhs.getRawLabels();
351 return std::lexicographical_compare(ours.rbegin(), ours.rend(), rhsLabels.rbegin(), rhsLabels.rend(), CIStringCompare());
352}
353
19164b93 354vector<std::string> DNSName::getRawLabels() const
3c115e0f 355{
19164b93 356 vector<std::string> ret;
ec3f33c6 357 ret.reserve(countLabels());
ae14c1f3 358 // 3www4ds9a2nl0
e358efcb
RG
359 for(const unsigned char* p = (const unsigned char*) d_storage.c_str(); p < ((const unsigned char*) d_storage.c_str()) + d_storage.size() && *p; p+=*p+1) {
360 ret.push_back({(const char*)p+1, (size_t)*p}); // XXX FIXME
361 }
97e5c6bc 362 return ret;
3c115e0f 363}
364
39c9bef5
RG
365std::string DNSName::getRawLabel(unsigned int pos) const
366{
367 unsigned int currentPos = 0;
368 for(const unsigned char* p = (const unsigned char*) d_storage.c_str(); p < ((const unsigned char*) d_storage.c_str()) + d_storage.size() && *p; p+=*p+1, currentPos++) {
369 if (currentPos == pos) {
370 return std::string((const char*)p+1, (size_t)*p);
371 }
372 }
373
374 throw std::out_of_range("trying to get label at position "+std::to_string(pos)+" of a DNSName that only has "+std::to_string(currentPos)+" labels");
375}
6d8bc3c6 376
41e01592
PL
377DNSName DNSName::getLastLabel() const
378{
379 DNSName ret(*this);
380 ret.trimToLabels(1);
381 return ret;
382}
383
21a3792f 384bool DNSName::chopOff()
3c115e0f 385{
ae14c1f3 386 if(d_storage.empty() || d_storage[0]==0)
3c115e0f 387 return false;
ec3f33c6 388 d_storage.erase(0, (unsigned int)d_storage[0]+1);
3c115e0f 389 return true;
390}
391
8aa5a28c
KM
392bool DNSName::isWildcard() const
393{
07338ade 394 if(d_storage.size() < 2)
8aa5a28c
KM
395 return false;
396 auto p = d_storage.begin();
397 return (*p == 0x01 && *++p == '*');
398}
399
32cd4eb1
PL
400/*
401 * Returns true if the DNSName is a valid RFC 1123 hostname, this function uses
402 * a regex on the string, so it is probably best not used when speed is essential.
403 */
404bool DNSName::isHostname() const
405{
406 static Regex hostNameRegex = Regex("^(([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)\\.)+$");
407 return hostNameRegex.match(this->toString());
408}
409
97e5c6bc 410unsigned int DNSName::countLabels() const
411{
412 unsigned int count=0;
19164b93
CHB
413 const unsigned char* p = reinterpret_cast<const unsigned char*>(d_storage.c_str());
414 const unsigned char* end = reinterpret_cast<const unsigned char*>(p + d_storage.size());
415
416 while (p < end && *p) {
97e5c6bc 417 ++count;
19164b93
CHB
418 p += *p + 1;
419 }
97e5c6bc 420 return count;
421}
422
c9262563 423void DNSName::trimToLabels(unsigned int to)
424{
97e5c6bc 425 while(countLabels() > to && chopOff())
c9262563 426 ;
427}
428
3c115e0f 429
5e1031cd
PD
430size_t hash_value(DNSName const& d)
431{
76cca09f 432 return d.hash();
5e1031cd
PD
433}
434
2430e53c 435void DNSName::appendEscapedLabel(std::string& appendTo, const char* orig, size_t len)
3c115e0f 436{
19164b93
CHB
437 size_t pos = 0;
438
19164b93
CHB
439 while (pos < len) {
440 auto p = static_cast<uint8_t>(orig[pos]);
950cfe0f 441 if(p=='.')
2430e53c 442 appendTo+="\\.";
3c115e0f 443 else if(p=='\\')
2430e53c 444 appendTo+="\\\\";
568556f8 445 else if(p > 0x20 && p < 0x7f)
2430e53c 446 appendTo.append(1, (char)p);
3c115e0f 447 else {
2430e53c
RG
448 char buf[] = "000";
449 auto got = snprintf(buf, sizeof(buf), "%03" PRIu8, p);
450 if (got < 0 || static_cast<size_t>(got) >= sizeof(buf)) {
451 throw std::runtime_error("Error, snprintf returned " + std::to_string(got) + " while escaping label " + std::string(orig, len));
452 }
453 appendTo.append(1, '\\');
454 appendTo += buf;
3c115e0f 455 }
19164b93 456 ++pos;
3c115e0f 457 }
3c115e0f 458}
bf7ef5b4
RG
459
460bool DNSName::has8bitBytes() const
461{
462 const auto& s = d_storage;
463 string::size_type pos = 0;
464 uint8_t length = s.at(pos);
465 while (length > 0) {
466 for (size_t idx = 0; idx < length; idx++) {
467 ++pos;
468 char c = s.at(pos);
469 if(!((c >= 'a' && c <= 'z') ||
470 (c >= 'A' && c <= 'Z') ||
471 (c >= '0' && c <= '9') ||
472 c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@' || c==' ' || c=='\\' || c==':'))
473 return true;
474 }
475 ++pos;
476 length = s.at(pos);
477 }
478
479 return false;
480}