]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsname.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
23 #include <boost/format.hpp>
27 #include "dnswriter.hh"
30 #include <boost/functional/hash.hpp>
32 const DNSName
g_rootdnsname("."), g_wildcarddnsname("*");
35 in DNS label format, with trailing 0. W/o trailing 0, we are 'empty'
36 www.powerdns.com = 3www8powerdns3com0
39 std::ostream
& operator<<(std::ostream
&os
, const DNSName
& d
)
41 return os
<<d
.toLogString();
44 DNSName::DNSName(const char* p
)
46 if(p
[0]==0 || (p
[0]=='.' && p
[1]==0)) {
47 d_storage
.assign(1, (char)0);
49 if(!strchr(p
, '\\')) {
50 unsigned char lenpos
=0;
51 unsigned char labellen
=0;
52 size_t plen
=strlen(p
);
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();
58 throw std::runtime_error("Found . in wrong position in DNSName "+string(p
));
59 d_storage
.append(1, (char)0);
62 for(; iter
!= pend
&& *iter
!='.'; ++iter
) {
65 d_storage
.append(begiter
,iter
);
69 throw std::range_error("label too long to append");
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");
74 d_storage
[lenpos
]=labellen
;
76 d_storage
.append(1, (char)0);
79 d_storage
=segmentDNSNameRaw(p
);
80 if(d_storage
.size() > 255) {
81 throw std::range_error("name too long");
88 DNSName::DNSName(const char* pos
, int len
, int offset
, bool uncompress
, uint16_t* qtype
, uint16_t* qclass
, unsigned int* consumed
, uint16_t minOffset
)
91 throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset
)+ " >= "+std::to_string(len
)+")");
94 if(const void * fnd
=memchr(pos
+offset
, 0, len
-offset
)) {
95 d_storage
.reserve(2+(const char*)fnd
-(pos
+offset
));
99 packetParser(pos
, len
, offset
, uncompress
, qtype
, qclass
, consumed
, 0, minOffset
);
102 // this should be the __only__ dns name parser in PowerDNS.
103 void 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
)
105 const unsigned char* pos
=(const unsigned char*)qpos
;
106 unsigned char labellen
;
107 const unsigned char *opos
= pos
;
110 throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset
)+ " >= "+std::to_string(len
)+")");
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
)+")");
114 const unsigned char* end
= pos
+ len
;
116 while((labellen
=*pos
++) && pos
< end
) { // "scan and copy"
117 if(labellen
>= 0xc0) {
119 throw std::range_error("Found compressed label, instructed not to follow");
122 int newpos
= (labellen
<< 8) + *(const unsigned char*)pos
;
124 if(newpos
< offset
) {
125 if(newpos
< (int) minOffset
)
126 throw std::range_error("Invalid label position during decompression ("+std::to_string(newpos
)+ " < "+std::to_string(minOffset
)+")");
128 throw std::range_error("Abort label decompression after 100 redirects");
129 packetParser((const char*)opos
, len
, newpos
, true, 0, 0, 0, depth
, minOffset
);
131 throw std::range_error("Found a forward reference during label decompression");
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)");
137 if (pos
+ labellen
< end
) {
138 appendRawLabel((const char*)pos
, labellen
);
141 throw std::range_error("Found an invalid label length in qname");
144 if(d_storage
.empty())
145 d_storage
.append(1, (char)0); // we just parsed the root
147 *consumed
= pos
- opos
- offset
;
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
)+")");
152 *qtype
=(*(const unsigned char*)pos
)*256 + *((const unsigned char*)pos
+1);
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
)+")");
159 *qclass
=(*(const unsigned char*)pos
)*256 + *((const unsigned char*)pos
+1);
163 std::string
DNSName::toString(const std::string
& separator
, const bool trailing
) const
166 throw std::out_of_range("Attempt to print an unset dnsname");
170 return trailing
? separator
: "";
173 ret
.reserve(d_storage
.size());
176 // iterate over the raw labels
177 const char* p
= d_storage
.c_str();
178 const char* end
= p
+ d_storage
.size();
180 while (p
< end
&& *p
) {
181 appendEscapedLabel(ret
, p
+ 1, static_cast<size_t>(*p
));
187 ret
.resize(ret
.size() - separator
.size());
192 std::string
DNSName::toLogString() const
198 return toStringRootDot();
201 std::string
DNSName::toDNSString() const
204 throw std::out_of_range("Attempt to DNSString an unset dnsname");
206 return std::string(d_storage
.c_str(), d_storage
.length());
209 std::string
DNSName::toDNSStringLC() const
211 return toLower(toDNSString()); // label lengths are always < 'A'
215 * Get the length of the DNSName on the wire
217 * @return the total wirelength of the DNSName
219 size_t DNSName::wirelength() const {
220 return d_storage
.length();
223 // Are WE part of parent
224 bool DNSName::isPartOf(const DNSName
& parent
) const
226 if(parent
.empty() || empty())
227 throw std::out_of_range("empty dnsnames aren't part of anything");
229 if(parent
.d_storage
.size() > d_storage
.size())
232 // this is slightly complicated since we can't start from the end, since we can't see where a label begins/ends then
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()) {
238 if (static_cast<size_t>(distance
) == parent
.d_storage
.size()) {
239 auto p
= parent
.d_storage
.cbegin();
240 for(; us
!= d_storage
.cend(); ++us
, ++p
) {
241 if(dns_tolower(*p
) != dns_tolower(*us
))
247 throw std::out_of_range("negative label length in dnsname");
253 DNSName
DNSName::makeRelative(const DNSName
& zone
) const
256 ret
.makeUsRelative(zone
);
257 return ret
.empty() ? zone
: ret
; // HACK FIXME400
259 void DNSName::makeUsRelative(const DNSName
& zone
)
261 if (isPartOf(zone
)) {
262 d_storage
.erase(d_storage
.size()-zone
.d_storage
.size());
263 d_storage
.append(1, (char)0); // put back the trailing 0
269 DNSName
DNSName::getCommonLabels(const DNSName
& other
) const
273 const std::vector
<std::string
> ours
= getRawLabels();
274 const std::vector
<std::string
> others
= other
.getRawLabels();
276 for (size_t pos
= 0; ours
.size() > pos
&& others
.size() > pos
; pos
++) {
277 const std::string
& ourLabel
= ours
.at(ours
.size() - pos
- 1);
278 const std::string
& otherLabel
= others
.at(others
.size() - pos
- 1);
280 if (!pdns_iequals(ourLabel
, otherLabel
)) {
284 result
.prependRawLabel(ourLabel
);
290 DNSName
DNSName::labelReverse() const
295 return *this; // we don't create the root automatically below
298 vector
<string
> l
=getRawLabels();
300 ret
.appendRawLabel(l
.back());
307 void DNSName::appendRawLabel(const std::string
& label
)
309 appendRawLabel(label
.c_str(), label
.length());
312 void DNSName::appendRawLabel(const char* start
, unsigned int length
)
315 throw std::range_error("no such thing as an empty label to append");
317 throw std::range_error("label too long to append");
318 if(d_storage
.size() + length
> 254) // reserve one byte for the label length
319 throw std::range_error("name too long to append");
321 if(d_storage
.empty()) {
322 d_storage
.append(1, (char)length
);
325 *d_storage
.rbegin()=(char)length
;
327 d_storage
.append(start
, length
);
328 d_storage
.append(1, (char)0);
331 void DNSName::prependRawLabel(const std::string
& label
)
334 throw std::range_error("no such thing as an empty label to prepend");
335 if(label
.size() > 63)
336 throw std::range_error("label too long to prepend");
337 if(d_storage
.size() + label
.size() > 254) // reserve one byte for the label length
338 throw std::range_error("name too long to prepend");
340 if(d_storage
.empty())
341 d_storage
.append(1, (char)0);
343 string_t
prep(1, (char)label
.size());
344 prep
.append(label
.c_str(), label
.size());
345 d_storage
= prep
+d_storage
;
348 bool DNSName::slowCanonCompare(const DNSName
& rhs
) const
350 auto ours
=getRawLabels(), rhsLabels
= rhs
.getRawLabels();
351 return std::lexicographical_compare(ours
.rbegin(), ours
.rend(), rhsLabels
.rbegin(), rhsLabels
.rend(), CIStringCompare());
354 vector
<std::string
> DNSName::getRawLabels() const
356 vector
<std::string
> ret
;
357 ret
.reserve(countLabels());
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
365 std::string
DNSName::getRawLabel(unsigned int pos
) const
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
);
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");
377 DNSName
DNSName::getLastLabel() const
384 bool DNSName::chopOff()
386 if(d_storage
.empty() || d_storage
[0]==0)
388 d_storage
.erase(0, (unsigned int)d_storage
[0]+1);
392 bool DNSName::isWildcard() const
394 if(d_storage
.size() < 2)
396 auto p
= d_storage
.begin();
397 return (*p
== 0x01 && *++p
== '*');
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.
404 bool DNSName::isHostname() const
406 static Regex hostNameRegex
= Regex("^(([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)\\.)+$");
407 return hostNameRegex
.match(this->toString());
410 unsigned int DNSName::countLabels() const
412 unsigned int count
=0;
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());
416 while (p
< end
&& *p
) {
423 void DNSName::trimToLabels(unsigned int to
)
425 while(countLabels() > to
&& chopOff())
430 size_t hash_value(DNSName
const& d
)
435 void DNSName::appendEscapedLabel(std::string
& appendTo
, const char* orig
, size_t len
)
440 auto p
= static_cast<uint8_t>(orig
[pos
]);
445 else if(p
> 0x20 && p
< 0x7f)
446 appendTo
.append(1, (char)p
);
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
));
453 appendTo
.append(1, '\\');